From 152430fdfb8506083a3d9e9c05d15724fea9b0fe Mon Sep 17 00:00:00 2001 From: Oludare Oludare Date: Wed, 31 May 2017 16:19:28 -0400 Subject: [PATCH 01/34] Creation of harvest log API --- api/serializers.py | 8 ++++++-- api/urls.py | 3 +++ api/views/__init__.py | 1 + api/views/workflow.py | 4 +--- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index b4be9e01a..e272e24b8 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -5,7 +5,7 @@ from rest_framework_json_api import serializers from share import models -from share.models import ProviderRegistration, SiteBanner, CeleryTaskResult +from share.models import ProviderRegistration, SiteBanner, CeleryTaskResult, HarvestLogs from api import fields @@ -132,12 +132,16 @@ class Meta: 'is_active', 'gravatar', 'locale', 'time_zone' ) - class SourceSerializer(ShareModelSerializer): class Meta: model = models.Source fields = ('name', 'home_page', 'long_title', 'icon') +class HarvestLogSerializer(ShareModelSerializer): + class Meta: + model = models.HarvestLog + fields = '__all__' + class SiteBannerSerializer(ShareModelSerializer): color = serializers.SerializerMethodField() diff --git a/api/urls.py b/api/urls.py index ec60b1804..28b364806 100644 --- a/api/urls.py +++ b/api/urls.py @@ -14,6 +14,8 @@ from api.serializers import BaseShareSerializer from api.views.share import ShareObjectViewSet + + app_name = 'api' router = DefaultRouter() @@ -86,6 +88,7 @@ def register_url(self, subclass, viewset): register_route(r'rawdata', views.RawDatumViewSet) register_route(r'user', views.ShareUserViewSet) register_route(r'sources', views.SourceViewSet) +register_route(r'harvest', views.HarvestLogViewSet) router.register(r'normalizeddata', views.NormalizedDataViewSet, base_name='normalizeddata') diff --git a/api/views/__init__.py b/api/views/__init__.py index 68bec4096..9b557d372 100644 --- a/api/views/__init__.py +++ b/api/views/__init__.py @@ -5,3 +5,4 @@ from .registration import * # noqa from .schema import * # noqa from .banner import * # noqa +from .harvestlogs import * # noqa diff --git a/api/views/workflow.py b/api/views/workflow.py index 2261423a5..da78be3fb 100644 --- a/api/views/workflow.py +++ b/api/views/workflow.py @@ -14,8 +14,7 @@ from api.pagination import CursorPagination from api.authentication import APIV1TokenBackPortAuthentication from api.permissions import ReadOnlyOrTokenHasScopeOrIsAuthenticated -from api.serializers import FullNormalizedDataSerializer, BasicNormalizedDataSerializer, \ - RawDatumSerializer, ShareUserSerializer, SourceSerializer +from api.serializers import FullNormalizedDataSerializer, BasicNormalizedDataSerializer,RawDatumSerializer, ShareUserSerializer, SourceSerializer from share.models import RawDatum, NormalizedData, Source, SourceConfig, Transformer from share.tasks import disambiguate from share.harvest.serialization import DictSerializer @@ -44,7 +43,6 @@ class SourceViewSet(viewsets.ReadOnlyModelViewSet): def get_queryset(self): return Source.objects.exclude(icon='').exclude(is_deleted=True) - class NormalizedDataViewSet(viewsets.ModelViewSet): """View showing all normalized data in the SHARE Dataset. From 1603c6e7a0a0f592b157ba38973f29d07790aa26 Mon Sep 17 00:00:00 2001 From: Oludare Oludare Date: Wed, 31 May 2017 16:22:59 -0400 Subject: [PATCH 02/34] harvestlogs view file --- api/views/harvestlogs.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 api/views/harvestlogs.py diff --git a/api/views/harvestlogs.py b/api/views/harvestlogs.py new file mode 100644 index 000000000..79b68b6e5 --- /dev/null +++ b/api/views/harvestlogs.py @@ -0,0 +1,10 @@ +from rest_framework import viewsets + +from api.serializers import HarvestLogSerializer +from share.models import HarvestLog + +class HarvestLogViewSet(viewsets.ReadOnlyModelViewSet): + serializer_class = HarvestLogSerializer + + def get_queryset(self): + return HarvestLog.objects.all() From e68a541eb20521d32c42f666591a435e770d50f1 Mon Sep 17 00:00:00 2001 From: Oludare Oludare Date: Tue, 6 Jun 2017 09:48:56 -0400 Subject: [PATCH 03/34] Creating new endpoint for Source Config --- api/serializers.py | 4 ++++ api/urls.py | 1 + api/views/__init__.py | 1 + api/views/sourceConfig.py | 10 ++++++++++ 4 files changed, 16 insertions(+) create mode 100644 api/views/sourceConfig.py diff --git a/api/serializers.py b/api/serializers.py index e272e24b8..97fe3e886 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -142,6 +142,10 @@ class Meta: model = models.HarvestLog fields = '__all__' +class SourceConfigSerializer(ShareModelSerializer): + class Meta: + model = models.SourceConfig + fields = '__all__' class SiteBannerSerializer(ShareModelSerializer): color = serializers.SerializerMethodField() diff --git a/api/urls.py b/api/urls.py index 28b364806..d48896a64 100644 --- a/api/urls.py +++ b/api/urls.py @@ -89,6 +89,7 @@ def register_url(self, subclass, viewset): register_route(r'user', views.ShareUserViewSet) register_route(r'sources', views.SourceViewSet) register_route(r'harvest', views.HarvestLogViewSet) +register_route(r'sourceConfig', views.SourceConfigViewSet) router.register(r'normalizeddata', views.NormalizedDataViewSet, base_name='normalizeddata') diff --git a/api/views/__init__.py b/api/views/__init__.py index 9b557d372..ca4da1cb9 100644 --- a/api/views/__init__.py +++ b/api/views/__init__.py @@ -6,3 +6,4 @@ from .schema import * # noqa from .banner import * # noqa from .harvestlogs import * # noqa +from .sourceConfig import * # noqa diff --git a/api/views/sourceConfig.py b/api/views/sourceConfig.py new file mode 100644 index 000000000..7485a3021 --- /dev/null +++ b/api/views/sourceConfig.py @@ -0,0 +1,10 @@ +from rest_framework import viewsets + +from api.serializers import SourceConfigSerializer +from share.models import SourceConfig + +class SourceConfigViewSet(viewsets.ReadOnlyModelViewSet): + serializer_class = SourceConfigSerializer + + def get_queryset(self): + return SourceConfig.objects.all() From 21e06a919159941ff91500240532f498093b4b06 Mon Sep 17 00:00:00 2001 From: Oludare Oludare Date: Tue, 6 Jun 2017 11:18:42 -0400 Subject: [PATCH 04/34] fix atempt --- api/views/sourceConfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/views/sourceConfig.py b/api/views/sourceConfig.py index 7485a3021..ecc76f054 100644 --- a/api/views/sourceConfig.py +++ b/api/views/sourceConfig.py @@ -1,5 +1,5 @@ from rest_framework import viewsets - +# trying to fix shit from api.serializers import SourceConfigSerializer from share.models import SourceConfig From 7d9fd3b1d1d502a84a6dfac4d7dd282f55989f08 Mon Sep 17 00:00:00 2001 From: Oludare Oludare Date: Wed, 31 May 2017 16:19:28 -0400 Subject: [PATCH 05/34] Creation of harvest log API --- api/serializers.py | 10 +++++++--- api/urls.py | 3 +++ api/views/__init__.py | 1 + api/views/workflow.py | 4 +--- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index b4be9e01a..894ac8f84 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -5,10 +5,10 @@ from rest_framework_json_api import serializers from share import models -from share.models import ProviderRegistration, SiteBanner, CeleryTaskResult -from api import fields +from share.models import ProviderRegistration, SiteBanner, CeleryTaskResult, logs +from api import fields class BaseShareSerializer(serializers.ModelSerializer): @@ -132,12 +132,16 @@ class Meta: 'is_active', 'gravatar', 'locale', 'time_zone' ) - class SourceSerializer(ShareModelSerializer): class Meta: model = models.Source fields = ('name', 'home_page', 'long_title', 'icon') +class HarvestLogSerializer(ShareModelSerializer): + class Meta: + model = models.HarvestLog + fields = '__all__' + class SiteBannerSerializer(ShareModelSerializer): color = serializers.SerializerMethodField() diff --git a/api/urls.py b/api/urls.py index ec60b1804..28b364806 100644 --- a/api/urls.py +++ b/api/urls.py @@ -14,6 +14,8 @@ from api.serializers import BaseShareSerializer from api.views.share import ShareObjectViewSet + + app_name = 'api' router = DefaultRouter() @@ -86,6 +88,7 @@ def register_url(self, subclass, viewset): register_route(r'rawdata', views.RawDatumViewSet) register_route(r'user', views.ShareUserViewSet) register_route(r'sources', views.SourceViewSet) +register_route(r'harvest', views.HarvestLogViewSet) router.register(r'normalizeddata', views.NormalizedDataViewSet, base_name='normalizeddata') diff --git a/api/views/__init__.py b/api/views/__init__.py index 68bec4096..9b557d372 100644 --- a/api/views/__init__.py +++ b/api/views/__init__.py @@ -5,3 +5,4 @@ from .registration import * # noqa from .schema import * # noqa from .banner import * # noqa +from .harvestlogs import * # noqa diff --git a/api/views/workflow.py b/api/views/workflow.py index 2261423a5..da78be3fb 100644 --- a/api/views/workflow.py +++ b/api/views/workflow.py @@ -14,8 +14,7 @@ from api.pagination import CursorPagination from api.authentication import APIV1TokenBackPortAuthentication from api.permissions import ReadOnlyOrTokenHasScopeOrIsAuthenticated -from api.serializers import FullNormalizedDataSerializer, BasicNormalizedDataSerializer, \ - RawDatumSerializer, ShareUserSerializer, SourceSerializer +from api.serializers import FullNormalizedDataSerializer, BasicNormalizedDataSerializer,RawDatumSerializer, ShareUserSerializer, SourceSerializer from share.models import RawDatum, NormalizedData, Source, SourceConfig, Transformer from share.tasks import disambiguate from share.harvest.serialization import DictSerializer @@ -44,7 +43,6 @@ class SourceViewSet(viewsets.ReadOnlyModelViewSet): def get_queryset(self): return Source.objects.exclude(icon='').exclude(is_deleted=True) - class NormalizedDataViewSet(viewsets.ModelViewSet): """View showing all normalized data in the SHARE Dataset. From 4360e2ad883169d7204a5ad3c014c7499d0e8350 Mon Sep 17 00:00:00 2001 From: Oludare Oludare Date: Wed, 31 May 2017 16:22:59 -0400 Subject: [PATCH 06/34] harvestlogs view file --- api/views/harvestlogs.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 api/views/harvestlogs.py diff --git a/api/views/harvestlogs.py b/api/views/harvestlogs.py new file mode 100644 index 000000000..79b68b6e5 --- /dev/null +++ b/api/views/harvestlogs.py @@ -0,0 +1,10 @@ +from rest_framework import viewsets + +from api.serializers import HarvestLogSerializer +from share.models import HarvestLog + +class HarvestLogViewSet(viewsets.ReadOnlyModelViewSet): + serializer_class = HarvestLogSerializer + + def get_queryset(self): + return HarvestLog.objects.all() From 29f6ad1bcf3f219c6d2d5fc96b51cbf26064a25b Mon Sep 17 00:00:00 2001 From: Oludare Oludare Date: Tue, 6 Jun 2017 09:48:56 -0400 Subject: [PATCH 07/34] Creating new endpoint for Source Config --- api/serializers.py | 4 ++++ api/urls.py | 1 + api/views/__init__.py | 1 + api/views/sourceConfig.py | 10 ++++++++++ 4 files changed, 16 insertions(+) create mode 100644 api/views/sourceConfig.py diff --git a/api/serializers.py b/api/serializers.py index 894ac8f84..885782463 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -142,6 +142,10 @@ class Meta: model = models.HarvestLog fields = '__all__' +class SourceConfigSerializer(ShareModelSerializer): + class Meta: + model = models.SourceConfig + fields = '__all__' class SiteBannerSerializer(ShareModelSerializer): color = serializers.SerializerMethodField() diff --git a/api/urls.py b/api/urls.py index 28b364806..d48896a64 100644 --- a/api/urls.py +++ b/api/urls.py @@ -89,6 +89,7 @@ def register_url(self, subclass, viewset): register_route(r'user', views.ShareUserViewSet) register_route(r'sources', views.SourceViewSet) register_route(r'harvest', views.HarvestLogViewSet) +register_route(r'sourceConfig', views.SourceConfigViewSet) router.register(r'normalizeddata', views.NormalizedDataViewSet, base_name='normalizeddata') diff --git a/api/views/__init__.py b/api/views/__init__.py index 9b557d372..ca4da1cb9 100644 --- a/api/views/__init__.py +++ b/api/views/__init__.py @@ -6,3 +6,4 @@ from .schema import * # noqa from .banner import * # noqa from .harvestlogs import * # noqa +from .sourceConfig import * # noqa diff --git a/api/views/sourceConfig.py b/api/views/sourceConfig.py new file mode 100644 index 000000000..7485a3021 --- /dev/null +++ b/api/views/sourceConfig.py @@ -0,0 +1,10 @@ +from rest_framework import viewsets + +from api.serializers import SourceConfigSerializer +from share.models import SourceConfig + +class SourceConfigViewSet(viewsets.ReadOnlyModelViewSet): + serializer_class = SourceConfigSerializer + + def get_queryset(self): + return SourceConfig.objects.all() From 9af7821e69d20c68c7042b3679c3acd7edc042e2 Mon Sep 17 00:00:00 2001 From: Oludare Oludare Date: Tue, 6 Jun 2017 11:18:42 -0400 Subject: [PATCH 08/34] fix atempt --- api/views/sourceConfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/views/sourceConfig.py b/api/views/sourceConfig.py index 7485a3021..ecc76f054 100644 --- a/api/views/sourceConfig.py +++ b/api/views/sourceConfig.py @@ -1,5 +1,5 @@ from rest_framework import viewsets - +# trying to fix shit from api.serializers import SourceConfigSerializer from share.models import SourceConfig From 8f35eb365dfdf74c127dad30342012e799642949 Mon Sep 17 00:00:00 2001 From: Oludare Oludare Date: Fri, 9 Jun 2017 13:42:57 -0400 Subject: [PATCH 09/34] no message --- api/urls.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/urls.py b/api/urls.py index d48896a64..47daf496a 100644 --- a/api/urls.py +++ b/api/urls.py @@ -88,8 +88,8 @@ def register_url(self, subclass, viewset): register_route(r'rawdata', views.RawDatumViewSet) register_route(r'user', views.ShareUserViewSet) register_route(r'sources', views.SourceViewSet) -register_route(r'harvest', views.HarvestLogViewSet) -register_route(r'sourceConfig', views.SourceConfigViewSet) +register_route(r'HarvestLog', views.HarvestLogViewSet) +register_route(r'SourceConfig', views.SourceConfigViewSet) router.register(r'normalizeddata', views.NormalizedDataViewSet, base_name='normalizeddata') From 9fa4245e4719579a296a57a0ad3cb865285b786f Mon Sep 17 00:00:00 2001 From: Emily Carden Date: Tue, 6 Jun 2017 11:37:25 -0400 Subject: [PATCH 10/34] fixing source config --- share/harvest/base.py | 507 ++++++++++++++++++++---------------------- 1 file changed, 236 insertions(+), 271 deletions(-) diff --git a/share/harvest/base.py b/share/harvest/base.py index 18d1f516d..2f079f83c 100644 --- a/share/harvest/base.py +++ b/share/harvest/base.py @@ -1,288 +1,253 @@ -from hashlib import sha256 -import abc -import datetime -import logging -import types -import warnings - -import pendulum -import requests +import re +import sys +import copy from django.conf import settings -from django.utils import timezone - -from share.harvest.exceptions import HarvesterDisabledError -from share.harvest.ratelimit import RateLimittedProxy -from share.harvest.serialization import DeprecatedDefaultSerializer -from share.models import RawDatum +from django.contrib.contenttypes.models import ContentType +from django.db import DatabaseError +from django.db import models +from django.db import transaction +from django.db.models.base import ModelBase +from django.db.models.fields import AutoField +from django.utils.translation import ugettext_lazy as _ +from typedmodels import models as typedmodels -logger = logging.getLogger(__name__) +from db.deletion import DATABASE_CASCADE +from share import util +from share.models import fields +from share.models.change import Change +from share.models.fuzzycount import FuzzyCountManager +from share.models.indexes import ConcurrentIndex +from share.models.sql import ShareObjectManager -class FetchResult: - __slots__ = ('identifier', 'datum', 'datestamp', '_sha256') - @property - def sha256(self): - if not self._sha256: - self._sha256 = sha256(self.datum.encode('utf-8')).hexdigest() - return self._sha256 +class ShareObjectVersion(models.Model): + action = models.TextField(max_length=10) + objects = FuzzyCountManager() - def __init__(self, identifier, datum, datestamp=None): - self._sha256 = None - self.datestamp = datestamp - self.datum = datum - self.identifier = identifier + class Meta: + abstract = True + ordering = ('-date_modified', ) + base_manager_name = 'objects' def __repr__(self): - return '<{}({}, {}, {}...)>'.format(self.__class__.__name__, self.identifier, self.datestamp, self.sha256[:10]) - - -class BaseHarvester(metaclass=abc.ABCMeta): - """ - - Fetch: - Aquire and serialize data from a remote source, respecting rate limits. - fetch* methods return a generator that yield FetchResult objects - - Harvest: - Fetch and store data, respecting global rate limits. - harvest* methods return a generator that yield RawDatum objects - - """ - - SERIALIZER_CLASS = DeprecatedDefaultSerializer - - network_read_timeout = 30 - network_connect_timeout = 31 - - @property - def request_timeout(self): - """The timeout tuple for requests (connect, read) - """ - return (self.network_connect_timeout, self.network_read_timeout) - - def __init__(self, source_config, pretty=False, **kwargs): - """ - - Args: - source_config (SourceConfig): - pretty (bool, optional): Defaults to False. - **kwargs: Custom kwargs, generally from the source_config. Stored in self.kwargs - - """ - self.kwargs = kwargs - self.config = source_config - self.serializer = self.SERIALIZER_CLASS(pretty) - - self.session = requests.Session() - self.session.headers.update({'User-Agent': settings.SHARE_USER_AGENT}) - # TODO Make rate limit apply across threads - self.requests = RateLimittedProxy(self.session, self.config.rate_limit_allowance, self.config.rate_limit_period) - - self.network_read_timeout = kwargs.get('network_read_timeout', self.network_read_timeout) - self.network_connect_timeout = kwargs.get('network_connect_timeout', self.network_connect_timeout) - - def fetch_id(self, identifier): - """Fetch a document by provider ID. - - Optional to implement, intended for dev and manual testing. - - Args: - identifier (str): Unique ID the provider uses to identify works. - - Returns: - FetchResult - - """ - raise NotImplementedError('{!r} does not support fetching by ID'.format(self)) - - def fetch(self, today=False, **kwargs): - """Fetch data from today. - - Yields: - FetchResult - - """ - return self.fetch_date_range(datetime.date.today() - datetime.timedelta(days=1), datetime.date.today(), **kwargs) - - def fetch_date(self, date: datetime.date, **kwargs): - """Fetch data from the specified date. - - Yields: - FetchResult - """ - return self.fetch_date_range(date - datetime.timedelta(days=1), date, **kwargs) - - def fetch_date_range(self, start, end, limit=None, **kwargs): - """Fetch data from the specified date range. - - Yields: - FetchResult - - """ - if not isinstance(start, datetime.date): - raise TypeError('start must be a datetime.date. Got {!r}'.format(start)) - - if not isinstance(end, datetime.date): - raise TypeError('end must be a datetime.date. Got {!r}'.format(end)) - - if start >= end: - raise ValueError('start must be before end. {!r} > {!r}'.format(start, end)) - - if limit == 0: - return # No need to do anything - - # Cast to datetimes for compat reasons - start = pendulum.Pendulum.instance(datetime.datetime.combine(start, datetime.time(0, 0, 0, 0, timezone.utc))) - end = pendulum.Pendulum.instance(datetime.datetime.combine(end, datetime.time(0, 0, 0, 0, timezone.utc))) - - if hasattr(self, 'shift_range'): - warnings.warn( - '{!r} implements a deprecated interface. ' - 'Handle date transforms in _do_fetch. ' - 'shift_range will no longer be called in SHARE 2.9.0'.format(self), - DeprecationWarning + return '<{type}({id}, of {persistent_id} at {date_modified})>'.format( + type=type(self).__name__, + id=self.id, + persistent_id=self.persistent_id_id, + date_modified=self.date_modified, + ) + + +# Generates 2 class from the original definition of the model +# A concrete class, +# And a version class, Version +class ShareObjectMeta(ModelBase): + concrete_bases = () + version_bases = (ShareObjectVersion, ) + + # This if effectively the "ShareBaseClass" + # Due to limitations in Django and TypedModels we cannot have an actual inheritance chain + share_attrs = { + 'change': lambda: models.OneToOneField(Change, related_name='affected_%(class)s', editable=False, on_delete=DATABASE_CASCADE), + 'date_modified': lambda: models.DateTimeField(auto_now=True, editable=False, db_index=True, help_text=_('The date this record was modified by SHARE.')), + 'date_created': lambda: models.DateTimeField(auto_now_add=True, editable=False, help_text=_('The date of ingress to SHARE.')), + } + + def __new__(cls, name, bases, attrs): + if (models.Model in bases and attrs['Meta'].abstract) or len(bases) > 1: + return super(ShareObjectMeta, cls).__new__(cls, name, bases, attrs) + + version_attrs = {} + for key, val in attrs.items(): + if isinstance(val, models.Field) and (val.unique or val.db_index): + val = copy.deepcopy(val) + val._unique = False + val.db_index = False + if isinstance(val, models.Field) and val.is_relation: + val = copy.deepcopy(val) + if isinstance(val, models.ForeignKey) and not isinstance(val, fields.ShareForeignKey): + val.remote_field.related_name = '+' + if isinstance(val, (fields.ShareForeignKey, fields.ShareManyToManyField, fields.ShareOneToOneField)): + val._kwargs = {**val._kwargs, 'related_name': '+', 'db_index': False} + if key == 'Meta': + val = type('VersionMeta', (val, ShareObjectVersion.Meta), {'unique_together': None, 'db_table': val.db_table + 'version' if hasattr(val, 'db_table') else None}) + version_attrs[key] = val + + # TODO Fix this in some non-horrid fashion + if name != 'ExtraData': + version_attrs['extra'] = fields.ShareForeignKey('ExtraData', null=True) + + version = super(ShareObjectMeta, cls).__new__(cls, name + 'Version', cls.version_bases, { + **version_attrs, + **cls.share_attrs, + **{k: v() for k, v in cls.share_attrs.items()}, # Excluded sources from versions. They never get filled out + 'persistent_id': models.ForeignKey(name, db_column='persistent_id', related_name='versions', on_delete=DATABASE_CASCADE), + '__qualname__': attrs['__qualname__'] + 'Version', + 'same_as': fields.ShareForeignKey(name, null=True, related_name='+'), + }) + + if name != 'ExtraData': + attrs['extra'] = fields.ShareOneToOneField('ExtraData', null=True) + + concrete = super(ShareObjectMeta, cls).__new__(cls, name, (bases[0], ) + cls.concrete_bases, { + **attrs, + **{k: v() for k, v in cls.share_attrs.items()}, + 'VersionModel': version, + 'same_as': fields.ShareForeignKey(name, null=True, related_name='+'), + 'version': models.OneToOneField(version, editable=False, related_name='%(app_label)s_%(class)s_version', on_delete=DATABASE_CASCADE), + # TypedManyToManyField works just like a normal field but has some special code to handle proxy models (if the exist) + # and makes the database use ON DELETE CASCADE as opposed to Djangos software cascade + 'sources': fields.TypedManyToManyField(settings.AUTH_USER_MODEL, related_name='source_%(class)s', editable=False), + }) + + # Inject Version into the module of the original class definition + __import__(concrete.__module__) + setattr(sys.modules[concrete.__module__], concrete.VersionModel.__name__, concrete.VersionModel) + + return concrete + + +class TypedShareObjectMeta(ShareObjectMeta, typedmodels.TypedModelMetaclass): + concrete_bases = (typedmodels.TypedModel,) + version_bases = (ShareObjectVersion, typedmodels.TypedModel) + + def __new__(cls, name, bases, attrs): + # Any subclasses of a class that already uses this metaclass will be + # turned into a proxy to the original table via TypedModelMetaclass + if ShareObject not in bases: + version = typedmodels.TypedModelMetaclass.__new__(cls, name + 'Version', (bases[0].VersionModel, ), { + **attrs, + '__qualname__': attrs['__qualname__'] + 'Version' + }) + + # Our triggers don't update django typed's type field. + # Makes the concrete type option resolve properly when loading versions from the db + # And forces queries to use the concrete models key + version._typedmodels_type = 'share.' + name.lower() + version._typedmodels_registry['share.' + name.lower()] = version + + return typedmodels.TypedModelMetaclass.__new__(cls, name, bases, {**attrs, 'VersionModel': version}) + return super(TypedShareObjectMeta, cls).__new__(cls, name, bases, attrs) + + +class ExtraData(models.Model, metaclass=ShareObjectMeta): + data = fields.DateTimeAwareJSONField(default=dict) + + objects = FuzzyCountManager() + + class Meta: + abstract = False + + +class ShareObject(models.Model, metaclass=ShareObjectMeta): + id = models.AutoField(primary_key=True) + objects = ShareObjectManager() + changes = fields.GenericRelationNoCascade('Change', related_query_name='share_objects', content_type_field='target_type', object_id_field='target_id', for_concrete_model=True) + + class Meta: + abstract = True + base_manager_name = 'objects' + indexes = [ + ConcurrentIndex(fields=['date_created']) + ] + + def get_absolute_url(self): + return '{}{}/{}'.format(settings.SHARE_WEB_URL, self._meta.model_name, util.IDObfuscator.encode(self)) + + def administrative_change(self, allow_empty=False, **kwargs): + from share.models import Change + from share.models import ChangeSet + from share.models import NormalizedData + from share.models import ShareUser + + with transaction.atomic(): + if not kwargs and not allow_empty: + # Empty changes can be made to force modified_date to update + raise ValueError('Pass allow_empty=True to allow empty changes') + + serialized = {} + for key, value in tuple(kwargs.items()): + if isinstance(value, ShareObject): + serialized[key] = {'@id': util.IDObfuscator.encode(value), '@type': value._meta.model_name} + else: + serialized[key] = value + + nd = NormalizedData.objects.create( + source=ShareUser.objects.get(username='system'), + data={ + '@graph': [{'@id': self.pk, '@type': self._meta.model_name, **serialized}] + } ) - start, end = self.shift_range(start, end) - - data_gen = self._do_fetch(start, end, **kwargs) - - if not isinstance(data_gen, types.GeneratorType) and len(data_gen) != 0: - raise TypeError('{!r}._do_fetch must return a GeneratorType for optimal performance and memory usage'.format(self)) - - for i, blob in enumerate(data_gen): - result = FetchResult(blob[0], self.serializer.serialize(blob[1]), *blob[2:]) - - if result.datestamp and (result.datestamp.date() < start.date() or result.datestamp.date() > end.date()): - raise ValueError( - 'result.datestamp is outside of the requested date range. ' - '{} from {} is not within [{} - {}]'.format(result.datestamp, result.identifier, start, end) - ) - yield result + cs = ChangeSet.objects.create(normalized_data=nd, status=ChangeSet.STATUS.accepted) + change = Change.objects.create(change={}, node_id=str(self.pk), type=Change.TYPE.update, target=self, target_version=self.version, change_set=cs, model_type=ContentType.objects.get_for_model(type(self))) - if limit is not None and i >= limit: - break + acceptable_fields = set(f.name for f in self._meta.get_fields()) + for key, value in kwargs.items(): + if key not in acceptable_fields: + raise AttributeError(key) + setattr(self, key, value) + self.change = change - def harvest_id(self, identifier): - """Harvest a document by ID. - - Note: - Dependant on whether or not fetch_id is implemented. - - Args: - identifier (str): Unique ID the provider uses to identify works. - - Returns: - RawDatum + self.save() + # NOTE/TODO Version will be popluated when a share object is first created + # Updating a share object WILL NOT update the version + def _save_table(self, raw=False, cls=None, force_insert=False, force_update=False, using=None, update_fields=None): """ - return RawDatum.objects.store_data(self.config, self.fetch_by_id(identifier)) - - def harvest(self, **kwargs): - """Fetch data from yesterday. - - Yields: - RawDatum - + Does the heavy-lifting involved in saving. Updates or inserts the data + for a single table. """ - return self.harvest_date(datetime.date.today(), **kwargs) - - def harvest_date(self, date, **kwargs): - """Harvest data from the specified date. - - Yields: - RawDatum - - """ - return self.harvest_date_range(date - datetime.timedelta(days=1), date, **kwargs) - - def harvest_date_range(self, start, end, limit=None, force=False, ignore_disabled=False, **kwargs): - """Fetch data from the specified date range. - - Args: - start (date): - end (date): - limit (int, optional): The maximum number of unique data to harvest. Defaults to None. - Uniqueness is determined by the SHA-256 of the raw data - force (bool, optional): Disable all safety checks, unexpected exceptions will still be raised. Defaults to False. - ignore_disabled (bool, optional): Don't check if this Harvester or Source is disabled or deleted. Defaults to False. - **kwargs: Forwared to _do_fetch. - - Yields: - RawDatum - - """ - if self.serializer.pretty: - raise ValueError('To ensure that data is optimally deduplicated, harvests may not occur while using a pretty serializer.') - - if (self.config.disabled or self.config.source.is_deleted) and not (force or ignore_disabled): - raise HarvesterDisabledError('Harvester {!r} is disabled. Either enable it, run with force=True, or ignore_disabled=True.'.format(self.config)) - - with self.config.acquire_lock(required=not force): - logger.info('Harvesting %s - %s from %r', start, end, self.config) - yield from RawDatum.objects.store_chunk(self.config, self.fetch_date_range(start, end, **kwargs), limit=limit) - - def harvest_from_log(self, harvest_log, **kwargs): - """Harvest data as specified by the given harvest_log. - - Args: - harvest_log (HarvestLog): The HarvestLog that describes the parameters of this harvest - limit (int, optional): The maximum number of unique data to harvest. Defaults to None. - **kwargs: Forwared to harvest_date_range. - - Yields: - RawDatum - - """ - error = None - datum_ids = [] - logger.info('Harvesting %r', harvest_log) - - with harvest_log.handle(self.VERSION): - try: - for datum in self.harvest_date_range(harvest_log.start_date, harvest_log.end_date, **kwargs): - datum_ids.append(datum.id) - yield datum - except Exception as e: - error = e - raise error - finally: - try: - harvest_log.raw_data.add(*datum_ids) - except Exception as e: - logger.exception('Failed to connection %r to raw data', harvest_log) - # Avoid shadowing the original error - if not error: - raise e - - def _do_fetch(self, start, end, **kwargs): - """Fetch date from this source inside of the given date range. - - The given date range should be treated as [start, end) - - Any HTTP[S] requests MUST be sent using the self.requests client. - It will automatically enforce rate limits - - Args: - start_date (datetime): Date to start fetching data from, inclusively. - end_date (datetime): Date to fetch data up to, exclusively. - **kwargs: Arbitrary kwargs passed to subclasses, used to customize harvesting. - - Returns: - Iterator: The fetched data. - - """ - if hasattr(self, 'do_harvest'): - warnings.warn( - '{!r} implements a deprecated interface. ' - 'do_harvest has been replaced by _do_fetch for clarity. ' - 'do_harvest will no longer be called in SHARE 2.11.0'.format(self), - DeprecationWarning - ) - logger.warning('%r implements a deprecated interface. ', self) - return self.do_harvest(start, end, **kwargs) - - raise NotImplementedError() + meta = cls._meta + non_pks = [f for f in meta.local_concrete_fields if not f.primary_key] + + if update_fields: + non_pks = [f for f in non_pks + if f.name in update_fields or f.attname in update_fields] + + pk_val = self._get_pk_val(meta) + if pk_val is None: + pk_val = meta.pk.get_pk_value_on_save(self) + setattr(self, meta.pk.attname, pk_val) + pk_set = pk_val is not None + if not pk_set and (force_update or update_fields): + raise ValueError("Cannot force an update in save() with no primary key.") + updated = False + # If possible, try an UPDATE. If that doesn't update anything, do an INSERT. + if pk_set and not force_insert: + base_qs = cls._base_manager.using(using) + values = [(f, None, (getattr(self, f.attname) if raw else f.pre_save(self, False))) + for f in non_pks] + forced_update = update_fields or force_update + updated = self._do_update(base_qs, using, pk_val, values, update_fields, + forced_update) + if force_update and not updated: + raise DatabaseError("Forced update did not affect any rows.") + if update_fields and not updated: + raise DatabaseError("Save with update_fields did not affect any rows.") + if not updated: + if meta.order_with_respect_to: + # If this is a model with an order_with_respect_to + # autopopulate the _order field + field = meta.order_with_respect_to + filter_args = field.get_filter_kwargs_for_object(self) + order_value = cls._base_manager.using(using).filter(**filter_args).count() + self._order = order_value + + fields = meta.local_concrete_fields + if not pk_set: + fields = [f for f in fields if not isinstance(f, AutoField)] + + update_pk = bool(meta.auto_field is not None and not pk_set) + result = self._do_insert(cls._base_manager, using, fields, update_pk, raw) + if update_pk: + ### ACTUAL CHANGE HERE ### + # Use regex as it will, hopefully, fail if something get messed up + pk, version_id = re.match(r'\((\d+),(\d+)\)', result).groups() + setattr(self, meta.pk.attname, int(pk)) + setattr(self, meta.get_field('version').attname, int(version_id)) + ### /ACTUAL CHANGE HERE ### + return updated From d7320cf151c321289e9732233514e4543bf9cf6c Mon Sep 17 00:00:00 2001 From: Emily Carden Date: Tue, 6 Jun 2017 11:47:39 -0400 Subject: [PATCH 11/34] no message --- share/harvest/base.py | 433 ++++++++++++++++++------------------------ 1 file changed, 188 insertions(+), 245 deletions(-) diff --git a/share/harvest/base.py b/share/harvest/base.py index 2f079f83c..ffd727e99 100644 --- a/share/harvest/base.py +++ b/share/harvest/base.py @@ -1,253 +1,196 @@ -import re -import sys -import copy +import abc +import json +import time +import types +import logging +import datetime +from typing import Tuple +from typing import Union +from typing import Iterator + +import pendulum +import requests from django.conf import settings -from django.contrib.contenttypes.models import ContentType -from django.db import DatabaseError -from django.db import models -from django.db import transaction -from django.db.models.base import ModelBase -from django.db.models.fields import AutoField -from django.utils.translation import ugettext_lazy as _ - -from typedmodels import models as typedmodels - -from db.deletion import DATABASE_CASCADE - -from share import util -from share.models import fields -from share.models.change import Change -from share.models.fuzzycount import FuzzyCountManager -from share.models.indexes import ConcurrentIndex -from share.models.sql import ShareObjectManager - - -class ShareObjectVersion(models.Model): - action = models.TextField(max_length=10) - objects = FuzzyCountManager() - - class Meta: - abstract = True - ordering = ('-date_modified', ) - base_manager_name = 'objects' - - def __repr__(self): - return '<{type}({id}, of {persistent_id} at {date_modified})>'.format( - type=type(self).__name__, - id=self.id, - persistent_id=self.persistent_id_id, - date_modified=self.date_modified, - ) - - -# Generates 2 class from the original definition of the model -# A concrete class, -# And a version class, Version -class ShareObjectMeta(ModelBase): - concrete_bases = () - version_bases = (ShareObjectVersion, ) - - # This if effectively the "ShareBaseClass" - # Due to limitations in Django and TypedModels we cannot have an actual inheritance chain - share_attrs = { - 'change': lambda: models.OneToOneField(Change, related_name='affected_%(class)s', editable=False, on_delete=DATABASE_CASCADE), - 'date_modified': lambda: models.DateTimeField(auto_now=True, editable=False, db_index=True, help_text=_('The date this record was modified by SHARE.')), - 'date_created': lambda: models.DateTimeField(auto_now_add=True, editable=False, help_text=_('The date of ingress to SHARE.')), - } - - def __new__(cls, name, bases, attrs): - if (models.Model in bases and attrs['Meta'].abstract) or len(bases) > 1: - return super(ShareObjectMeta, cls).__new__(cls, name, bases, attrs) - - version_attrs = {} - for key, val in attrs.items(): - if isinstance(val, models.Field) and (val.unique or val.db_index): - val = copy.deepcopy(val) - val._unique = False - val.db_index = False - if isinstance(val, models.Field) and val.is_relation: - val = copy.deepcopy(val) - if isinstance(val, models.ForeignKey) and not isinstance(val, fields.ShareForeignKey): - val.remote_field.related_name = '+' - if isinstance(val, (fields.ShareForeignKey, fields.ShareManyToManyField, fields.ShareOneToOneField)): - val._kwargs = {**val._kwargs, 'related_name': '+', 'db_index': False} - if key == 'Meta': - val = type('VersionMeta', (val, ShareObjectVersion.Meta), {'unique_together': None, 'db_table': val.db_table + 'version' if hasattr(val, 'db_table') else None}) - version_attrs[key] = val - - # TODO Fix this in some non-horrid fashion - if name != 'ExtraData': - version_attrs['extra'] = fields.ShareForeignKey('ExtraData', null=True) - - version = super(ShareObjectMeta, cls).__new__(cls, name + 'Version', cls.version_bases, { - **version_attrs, - **cls.share_attrs, - **{k: v() for k, v in cls.share_attrs.items()}, # Excluded sources from versions. They never get filled out - 'persistent_id': models.ForeignKey(name, db_column='persistent_id', related_name='versions', on_delete=DATABASE_CASCADE), - '__qualname__': attrs['__qualname__'] + 'Version', - 'same_as': fields.ShareForeignKey(name, null=True, related_name='+'), - }) - - if name != 'ExtraData': - attrs['extra'] = fields.ShareOneToOneField('ExtraData', null=True) - - concrete = super(ShareObjectMeta, cls).__new__(cls, name, (bases[0], ) + cls.concrete_bases, { - **attrs, - **{k: v() for k, v in cls.share_attrs.items()}, - 'VersionModel': version, - 'same_as': fields.ShareForeignKey(name, null=True, related_name='+'), - 'version': models.OneToOneField(version, editable=False, related_name='%(app_label)s_%(class)s_version', on_delete=DATABASE_CASCADE), - # TypedManyToManyField works just like a normal field but has some special code to handle proxy models (if the exist) - # and makes the database use ON DELETE CASCADE as opposed to Djangos software cascade - 'sources': fields.TypedManyToManyField(settings.AUTH_USER_MODEL, related_name='source_%(class)s', editable=False), - }) - - # Inject Version into the module of the original class definition - __import__(concrete.__module__) - setattr(sys.modules[concrete.__module__], concrete.VersionModel.__name__, concrete.VersionModel) - - return concrete - - -class TypedShareObjectMeta(ShareObjectMeta, typedmodels.TypedModelMetaclass): - concrete_bases = (typedmodels.TypedModel,) - version_bases = (ShareObjectVersion, typedmodels.TypedModel) - - def __new__(cls, name, bases, attrs): - # Any subclasses of a class that already uses this metaclass will be - # turned into a proxy to the original table via TypedModelMetaclass - if ShareObject not in bases: - version = typedmodels.TypedModelMetaclass.__new__(cls, name + 'Version', (bases[0].VersionModel, ), { - **attrs, - '__qualname__': attrs['__qualname__'] + 'Version' - }) - - # Our triggers don't update django typed's type field. - # Makes the concrete type option resolve properly when loading versions from the db - # And forces queries to use the concrete models key - version._typedmodels_type = 'share.' + name.lower() - version._typedmodels_registry['share.' + name.lower()] = version - - return typedmodels.TypedModelMetaclass.__new__(cls, name, bases, {**attrs, 'VersionModel': version}) - return super(TypedShareObjectMeta, cls).__new__(cls, name, bases, attrs) - - -class ExtraData(models.Model, metaclass=ShareObjectMeta): - data = fields.DateTimeAwareJSONField(default=dict) - - objects = FuzzyCountManager() - - class Meta: - abstract = False - - -class ShareObject(models.Model, metaclass=ShareObjectMeta): - id = models.AutoField(primary_key=True) - objects = ShareObjectManager() - changes = fields.GenericRelationNoCascade('Change', related_query_name='share_objects', content_type_field='target_type', object_id_field='target_id', for_concrete_model=True) - - class Meta: - abstract = True - base_manager_name = 'objects' - indexes = [ - ConcurrentIndex(fields=['date_created']) - ] - - def get_absolute_url(self): - return '{}{}/{}'.format(settings.SHARE_WEB_URL, self._meta.model_name, util.IDObfuscator.encode(self)) - - def administrative_change(self, allow_empty=False, **kwargs): - from share.models import Change - from share.models import ChangeSet - from share.models import NormalizedData - from share.models import ShareUser - - with transaction.atomic(): - if not kwargs and not allow_empty: - # Empty changes can be made to force modified_date to update - raise ValueError('Pass allow_empty=True to allow empty changes') - - serialized = {} - for key, value in tuple(kwargs.items()): - if isinstance(value, ShareObject): - serialized[key] = {'@id': util.IDObfuscator.encode(value), '@type': value._meta.model_name} - else: - serialized[key] = value - - nd = NormalizedData.objects.create( - source=ShareUser.objects.get(username='system'), - data={ - '@graph': [{'@id': self.pk, '@type': self._meta.model_name, **serialized}] - } - ) - cs = ChangeSet.objects.create(normalized_data=nd, status=ChangeSet.STATUS.accepted) - change = Change.objects.create(change={}, node_id=str(self.pk), type=Change.TYPE.update, target=self, target_version=self.version, change_set=cs, model_type=ContentType.objects.get_for_model(type(self))) - acceptable_fields = set(f.name for f in self._meta.get_fields()) - for key, value in kwargs.items(): - if key not in acceptable_fields: - raise AttributeError(key) - setattr(self, key, value) - self.change = change +logger = logging.getLogger(__name__) + + +class RateLimittedProxy: + + def __init__(self, proxy_to, calls, per_second): + self._proxy_to = proxy_to + self._allowance = calls + self._calls = calls + self._last_call = 0 + self._per_second = per_second + self._cache = {} + + def _check_limit(self): + if self._allowance > 1: + return + wait = self._per_second - (time.time() - self._last_call) + if wait > 0: + logger.debug('Rate limitting %s. Sleeping for %s', self._proxy_to, wait) + time.sleep(wait) + self._allowance = self._calls + logger.debug('Access granted for %s', self._proxy_to) + + def _called(self): + self._allowance -= 1 + self._last_call = time.time() + + def __call__(self, *args, **kwargs): + self._check_limit() + ret = self._proxy_to(*args, **kwargs) + self._called() + return ret + + def __getattr__(self, name): + return self._cache.setdefault(name, self.__class__(getattr(self._proxy_to, name), self._calls, self._per_second)) + + +class BaseHarvester(metaclass=abc.ABCMeta): - self.save() + def __init__(self, source_config, **kwargs): + self.last_call = 0 + self.new_raw_ids = [] + self.old_raw_ids = [] + self.config = source_config + self.kwargs = kwargs - # NOTE/TODO Version will be popluated when a share object is first created - # Updating a share object WILL NOT update the version - def _save_table(self, raw=False, cls=None, force_insert=False, force_update=False, using=None, update_fields=None): + session = requests.Session() + session.headers.update({'User-Agent': settings.SHARE_USER_AGENT}) + # TODO Make rate limit apply across threads + self.requests = RateLimittedProxy(session, self.config.rate_limit_allowance, self.config.rate_limit_period) + + @abc.abstractmethod + def do_harvest(self, start_date: pendulum.Pendulum, end_date: pendulum.Pendulum, **kwargs) -> Iterator[Tuple[str, Union[str, dict, bytes]]]: + """Fetch date from this provider inside of the given date range. + + Any HTTP[S] requests MUST be sent using the self.requests client. + It will automatically in force rate limits + + Args: + start_date (datetime): + end_date (datetime): + + Returns: + Iterator>: The fetched data paired with + the unique ID that this provider uses. + + [ + ('1', {'my': 'doc'}), + ('2', {'your': 'doc'}), + ] """ - Does the heavy-lifting involved in saving. Updates or inserts the data - for a single table. + raise NotImplementedError() + + def fetch_by_id(self, provider_id): """ - meta = cls._meta - non_pks = [f for f in meta.local_concrete_fields if not f.primary_key] - - if update_fields: - non_pks = [f for f in non_pks - if f.name in update_fields or f.attname in update_fields] - - pk_val = self._get_pk_val(meta) - if pk_val is None: - pk_val = meta.pk.get_pk_value_on_save(self) - setattr(self, meta.pk.attname, pk_val) - pk_set = pk_val is not None - if not pk_set and (force_update or update_fields): - raise ValueError("Cannot force an update in save() with no primary key.") - updated = False - # If possible, try an UPDATE. If that doesn't update anything, do an INSERT. - if pk_set and not force_insert: - base_qs = cls._base_manager.using(using) - values = [(f, None, (getattr(self, f.attname) if raw else f.pre_save(self, False))) - for f in non_pks] - forced_update = update_fields or force_update - updated = self._do_update(base_qs, using, pk_val, values, update_fields, - forced_update) - if force_update and not updated: - raise DatabaseError("Forced update did not affect any rows.") - if update_fields and not updated: - raise DatabaseError("Save with update_fields did not affect any rows.") - if not updated: - if meta.order_with_respect_to: - # If this is a model with an order_with_respect_to - # autopopulate the _order field - field = meta.order_with_respect_to - filter_args = field.get_filter_kwargs_for_object(self) - order_value = cls._base_manager.using(using).filter(**filter_args).count() - self._order = order_value - - fields = meta.local_concrete_fields - if not pk_set: - fields = [f for f in fields if not isinstance(f, AutoField)] - - update_pk = bool(meta.auto_field is not None and not pk_set) - result = self._do_insert(cls._base_manager, using, fields, update_pk, raw) - if update_pk: - ### ACTUAL CHANGE HERE ### - # Use regex as it will, hopefully, fail if something get messed up - pk, version_id = re.match(r'\((\d+),(\d+)\)', result).groups() - setattr(self, meta.pk.attname, int(pk)) - setattr(self, meta.get_field('version').attname, int(version_id)) - ### /ACTUAL CHANGE HERE ### - return updated + Fetch a document by provider ID. + + Optional to implement, intended for dev and manual testing. + + Args: + provider_id (str): Unique ID the provider uses to identify works. + + Returns: + str|dict|bytes: Fetched data. + + """ + raise NotImplementedError() + + def shift_range(self, start_date: pendulum.Pendulum, end_date: pendulum.Pendulum) -> pendulum.Pendulum: + """Most providers will not need this method. + + For providers that should be collecting data at an offset, see figshare. + + Args: + start_date (datetime): + end_date (datetime): + + Returns: + (datetime, datetime): The shifted date range + """ + return start_date, end_date + + def harvest(self, start_date: [datetime.datetime, datetime.timedelta, pendulum.Pendulum], end_date: [datetime.datetime, datetime.timedelta, pendulum.Pendulum], shift_range: bool=True, limit: int=None, **kwargs) -> list: + from share.models import RawDatum + start_date, end_date = self._validate_dates(start_date, end_date) + + rawdata = ((identifier, self.encode_data(datum)) for identifier, datum in self.do_harvest(start_date, end_date, **kwargs)) + assert isinstance(rawdata, types.GeneratorType), 'do_harvest did not return a generator type, found {!r}. Make sure to use the yield keyword'.format(type(rawdata)) + + yield from RawDatum.objects.store_chunk(self.config, rawdata, limit=limit) + + def raw(self, start_date: [datetime.datetime, datetime.timedelta, pendulum.Pendulum], end_date: [datetime.datetime, datetime.timedelta, pendulum.Pendulum], shift_range: bool=True, limit: int=None, **kwargs) -> list: + start_date, end_date = self._validate_dates(start_date, end_date) + count, harvest = 0, self.do_harvest(start_date, end_date, **kwargs) + assert isinstance(harvest, types.GeneratorType), 'do_harvest did not return a generator type, found {!r}. Make sure to use the yield keyword'.format(type(harvest)) + + for doc_id, datum in harvest: + yield doc_id, self.encode_data(datum, pretty=True) + count += 1 + if limit and count >= limit: + break + + def harvest_by_id(self, doc_id): + from share.models import RawDatum + datum = self.fetch_by_id(doc_id) + return RawDatum.objects.store_data(doc_id, self.encode_data(datum), self.config) + + def encode_data(self, data, pretty=False) -> str: + if isinstance(data, str): + return data + if isinstance(data, bytes): + logger.warning( + '%r.encode_data got a bytes instance. ' + 'do_harvest should be returning str types as only the harvester will know how to properly encode the bytes' + 'defaulting to decoding as utf-8', + self, + ) + return data.decode('utf-8') + if isinstance(data, dict): + return self.encode_json(data, pretty=pretty) + raise Exception('Unable to properly encode data blob {!r}. Data should be a dict, bytes, or str objects.'.format(data)) + + def encode_json(self, data: dict, pretty: bool=False) -> str: + """Orders a python dict recursively so it will always hash to the + same value. Used for Dedupping harvest results + Args: + data (dict): + + Returns: + str: json.dumpsed ordered dictionary + """ + return json.dumps(data, sort_keys=True, indent=4 if pretty else None) + + def _validate_dates(self, start_date, end_date): + assert not (bool(start_date) ^ bool(end_date)), 'Must specify both a start and end date or neither' + assert isinstance(start_date, (datetime.timedelta, datetime.datetime, pendulum.Pendulum)) and isinstance(end_date, (datetime.timedelta, datetime.datetime, pendulum.Pendulum)), 'start_date and end_date must be either datetimes or timedeltas' + assert not (isinstance(start_date, datetime.timedelta) and isinstance(end_date, datetime.timedelta)), 'Only one of start_date and end_date may be a timedelta' + + if isinstance(start_date, datetime.datetime): + start_date = pendulum.instance(start_date) + + if isinstance(end_date, datetime.datetime): + end_date = pendulum.instance(end_date) + + if isinstance(start_date, datetime.timedelta): + start_date = pendulum.instance(end_date + start_date) + + if isinstance(end_date, datetime.timedelta): + end_date = pendulum.instance(start_date + end_date) + + og_start, og_end = start_date, end_date + start_date, end_date = self.shift_range(start_date, end_date) + assert isinstance(start_date, pendulum.Pendulum) and isinstance(end_date, pendulum.Pendulum), 'transpose_time_window must return a tuple of 2 datetimes' + + if (og_start, og_end) != (start_date, end_date): + logger.warning('Date shifted from {} - {} to {} - {}. Disable shifting by passing shift_range=False'.format(og_start, og_end, start_date, end_date)) + + assert start_date < end_date, 'start_date must be before end_date {} < {}'.format(start_date, end_date) + + return start_date, end_date From 8055dbc60511a1c75a2ffaa32ccaefb89cfc8cca Mon Sep 17 00:00:00 2001 From: Oludare Oludare Date: Fri, 9 Jun 2017 13:42:57 -0400 Subject: [PATCH 12/34] no message --- api/urls.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/urls.py b/api/urls.py index d48896a64..47daf496a 100644 --- a/api/urls.py +++ b/api/urls.py @@ -88,8 +88,8 @@ def register_url(self, subclass, viewset): register_route(r'rawdata', views.RawDatumViewSet) register_route(r'user', views.ShareUserViewSet) register_route(r'sources', views.SourceViewSet) -register_route(r'harvest', views.HarvestLogViewSet) -register_route(r'sourceConfig', views.SourceConfigViewSet) +register_route(r'HarvestLog', views.HarvestLogViewSet) +register_route(r'SourceConfig', views.SourceConfigViewSet) router.register(r'normalizeddata', views.NormalizedDataViewSet, base_name='normalizeddata') From 7c54321c8aa0dc0ebc0866e7641e21c21072e73c Mon Sep 17 00:00:00 2001 From: Emily Carden Date: Wed, 14 Jun 2017 15:18:53 -0400 Subject: [PATCH 13/34] Checkout base.py --- api/serializers.py | 2 +- share/harvest/base.py | 358 ++++++++++++++++++++++++++---------------- 2 files changed, 226 insertions(+), 134 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index 97fe3e886..81edcae9f 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -5,7 +5,7 @@ from rest_framework_json_api import serializers from share import models -from share.models import ProviderRegistration, SiteBanner, CeleryTaskResult, HarvestLogs +from share.models import ProviderRegistration, SiteBanner, CeleryTaskResult, HarvestLog from api import fields diff --git a/share/harvest/base.py b/share/harvest/base.py index ffd727e99..18d1f516d 100644 --- a/share/harvest/base.py +++ b/share/harvest/base.py @@ -1,196 +1,288 @@ +from hashlib import sha256 import abc -import json -import time -import types -import logging import datetime -from typing import Tuple -from typing import Union -from typing import Iterator +import logging +import types +import warnings import pendulum import requests from django.conf import settings +from django.utils import timezone + +from share.harvest.exceptions import HarvesterDisabledError +from share.harvest.ratelimit import RateLimittedProxy +from share.harvest.serialization import DeprecatedDefaultSerializer +from share.models import RawDatum logger = logging.getLogger(__name__) -class RateLimittedProxy: +class FetchResult: + __slots__ = ('identifier', 'datum', 'datestamp', '_sha256') - def __init__(self, proxy_to, calls, per_second): - self._proxy_to = proxy_to - self._allowance = calls - self._calls = calls - self._last_call = 0 - self._per_second = per_second - self._cache = {} + @property + def sha256(self): + if not self._sha256: + self._sha256 = sha256(self.datum.encode('utf-8')).hexdigest() + return self._sha256 - def _check_limit(self): - if self._allowance > 1: - return - wait = self._per_second - (time.time() - self._last_call) - if wait > 0: - logger.debug('Rate limitting %s. Sleeping for %s', self._proxy_to, wait) - time.sleep(wait) - self._allowance = self._calls - logger.debug('Access granted for %s', self._proxy_to) + def __init__(self, identifier, datum, datestamp=None): + self._sha256 = None + self.datestamp = datestamp + self.datum = datum + self.identifier = identifier - def _called(self): - self._allowance -= 1 - self._last_call = time.time() + def __repr__(self): + return '<{}({}, {}, {}...)>'.format(self.__class__.__name__, self.identifier, self.datestamp, self.sha256[:10]) - def __call__(self, *args, **kwargs): - self._check_limit() - ret = self._proxy_to(*args, **kwargs) - self._called() - return ret - def __getattr__(self, name): - return self._cache.setdefault(name, self.__class__(getattr(self._proxy_to, name), self._calls, self._per_second)) +class BaseHarvester(metaclass=abc.ABCMeta): + """ + Fetch: + Aquire and serialize data from a remote source, respecting rate limits. + fetch* methods return a generator that yield FetchResult objects -class BaseHarvester(metaclass=abc.ABCMeta): + Harvest: + Fetch and store data, respecting global rate limits. + harvest* methods return a generator that yield RawDatum objects - def __init__(self, source_config, **kwargs): - self.last_call = 0 - self.new_raw_ids = [] - self.old_raw_ids = [] - self.config = source_config + """ + + SERIALIZER_CLASS = DeprecatedDefaultSerializer + + network_read_timeout = 30 + network_connect_timeout = 31 + + @property + def request_timeout(self): + """The timeout tuple for requests (connect, read) + """ + return (self.network_connect_timeout, self.network_read_timeout) + + def __init__(self, source_config, pretty=False, **kwargs): + """ + + Args: + source_config (SourceConfig): + pretty (bool, optional): Defaults to False. + **kwargs: Custom kwargs, generally from the source_config. Stored in self.kwargs + + """ self.kwargs = kwargs + self.config = source_config + self.serializer = self.SERIALIZER_CLASS(pretty) - session = requests.Session() - session.headers.update({'User-Agent': settings.SHARE_USER_AGENT}) + self.session = requests.Session() + self.session.headers.update({'User-Agent': settings.SHARE_USER_AGENT}) # TODO Make rate limit apply across threads - self.requests = RateLimittedProxy(session, self.config.rate_limit_allowance, self.config.rate_limit_period) + self.requests = RateLimittedProxy(self.session, self.config.rate_limit_allowance, self.config.rate_limit_period) - @abc.abstractmethod - def do_harvest(self, start_date: pendulum.Pendulum, end_date: pendulum.Pendulum, **kwargs) -> Iterator[Tuple[str, Union[str, dict, bytes]]]: - """Fetch date from this provider inside of the given date range. + self.network_read_timeout = kwargs.get('network_read_timeout', self.network_read_timeout) + self.network_connect_timeout = kwargs.get('network_connect_timeout', self.network_connect_timeout) - Any HTTP[S] requests MUST be sent using the self.requests client. - It will automatically in force rate limits + def fetch_id(self, identifier): + """Fetch a document by provider ID. + + Optional to implement, intended for dev and manual testing. Args: - start_date (datetime): - end_date (datetime): + identifier (str): Unique ID the provider uses to identify works. Returns: - Iterator>: The fetched data paired with - the unique ID that this provider uses. + FetchResult - [ - ('1', {'my': 'doc'}), - ('2', {'your': 'doc'}), - ] """ - raise NotImplementedError() + raise NotImplementedError('{!r} does not support fetching by ID'.format(self)) + + def fetch(self, today=False, **kwargs): + """Fetch data from today. + + Yields: + FetchResult - def fetch_by_id(self, provider_id): """ - Fetch a document by provider ID. + return self.fetch_date_range(datetime.date.today() - datetime.timedelta(days=1), datetime.date.today(), **kwargs) - Optional to implement, intended for dev and manual testing. + def fetch_date(self, date: datetime.date, **kwargs): + """Fetch data from the specified date. - Args: - provider_id (str): Unique ID the provider uses to identify works. + Yields: + FetchResult + """ + return self.fetch_date_range(date - datetime.timedelta(days=1), date, **kwargs) - Returns: - str|dict|bytes: Fetched data. + def fetch_date_range(self, start, end, limit=None, **kwargs): + """Fetch data from the specified date range. + + Yields: + FetchResult """ - raise NotImplementedError() + if not isinstance(start, datetime.date): + raise TypeError('start must be a datetime.date. Got {!r}'.format(start)) + + if not isinstance(end, datetime.date): + raise TypeError('end must be a datetime.date. Got {!r}'.format(end)) - def shift_range(self, start_date: pendulum.Pendulum, end_date: pendulum.Pendulum) -> pendulum.Pendulum: - """Most providers will not need this method. + if start >= end: + raise ValueError('start must be before end. {!r} > {!r}'.format(start, end)) - For providers that should be collecting data at an offset, see figshare. + if limit == 0: + return # No need to do anything + + # Cast to datetimes for compat reasons + start = pendulum.Pendulum.instance(datetime.datetime.combine(start, datetime.time(0, 0, 0, 0, timezone.utc))) + end = pendulum.Pendulum.instance(datetime.datetime.combine(end, datetime.time(0, 0, 0, 0, timezone.utc))) + + if hasattr(self, 'shift_range'): + warnings.warn( + '{!r} implements a deprecated interface. ' + 'Handle date transforms in _do_fetch. ' + 'shift_range will no longer be called in SHARE 2.9.0'.format(self), + DeprecationWarning + ) + start, end = self.shift_range(start, end) + + data_gen = self._do_fetch(start, end, **kwargs) + + if not isinstance(data_gen, types.GeneratorType) and len(data_gen) != 0: + raise TypeError('{!r}._do_fetch must return a GeneratorType for optimal performance and memory usage'.format(self)) + + for i, blob in enumerate(data_gen): + result = FetchResult(blob[0], self.serializer.serialize(blob[1]), *blob[2:]) + + if result.datestamp and (result.datestamp.date() < start.date() or result.datestamp.date() > end.date()): + raise ValueError( + 'result.datestamp is outside of the requested date range. ' + '{} from {} is not within [{} - {}]'.format(result.datestamp, result.identifier, start, end) + ) + + yield result + + if limit is not None and i >= limit: + break + + def harvest_id(self, identifier): + """Harvest a document by ID. + + Note: + Dependant on whether or not fetch_id is implemented. Args: - start_date (datetime): - end_date (datetime): + identifier (str): Unique ID the provider uses to identify works. Returns: - (datetime, datetime): The shifted date range + RawDatum + """ - return start_date, end_date + return RawDatum.objects.store_data(self.config, self.fetch_by_id(identifier)) - def harvest(self, start_date: [datetime.datetime, datetime.timedelta, pendulum.Pendulum], end_date: [datetime.datetime, datetime.timedelta, pendulum.Pendulum], shift_range: bool=True, limit: int=None, **kwargs) -> list: - from share.models import RawDatum - start_date, end_date = self._validate_dates(start_date, end_date) + def harvest(self, **kwargs): + """Fetch data from yesterday. - rawdata = ((identifier, self.encode_data(datum)) for identifier, datum in self.do_harvest(start_date, end_date, **kwargs)) - assert isinstance(rawdata, types.GeneratorType), 'do_harvest did not return a generator type, found {!r}. Make sure to use the yield keyword'.format(type(rawdata)) + Yields: + RawDatum - yield from RawDatum.objects.store_chunk(self.config, rawdata, limit=limit) + """ + return self.harvest_date(datetime.date.today(), **kwargs) - def raw(self, start_date: [datetime.datetime, datetime.timedelta, pendulum.Pendulum], end_date: [datetime.datetime, datetime.timedelta, pendulum.Pendulum], shift_range: bool=True, limit: int=None, **kwargs) -> list: - start_date, end_date = self._validate_dates(start_date, end_date) - count, harvest = 0, self.do_harvest(start_date, end_date, **kwargs) - assert isinstance(harvest, types.GeneratorType), 'do_harvest did not return a generator type, found {!r}. Make sure to use the yield keyword'.format(type(harvest)) + def harvest_date(self, date, **kwargs): + """Harvest data from the specified date. - for doc_id, datum in harvest: - yield doc_id, self.encode_data(datum, pretty=True) - count += 1 - if limit and count >= limit: - break + Yields: + RawDatum + + """ + return self.harvest_date_range(date - datetime.timedelta(days=1), date, **kwargs) + + def harvest_date_range(self, start, end, limit=None, force=False, ignore_disabled=False, **kwargs): + """Fetch data from the specified date range. - def harvest_by_id(self, doc_id): - from share.models import RawDatum - datum = self.fetch_by_id(doc_id) - return RawDatum.objects.store_data(doc_id, self.encode_data(datum), self.config) - - def encode_data(self, data, pretty=False) -> str: - if isinstance(data, str): - return data - if isinstance(data, bytes): - logger.warning( - '%r.encode_data got a bytes instance. ' - 'do_harvest should be returning str types as only the harvester will know how to properly encode the bytes' - 'defaulting to decoding as utf-8', - self, - ) - return data.decode('utf-8') - if isinstance(data, dict): - return self.encode_json(data, pretty=pretty) - raise Exception('Unable to properly encode data blob {!r}. Data should be a dict, bytes, or str objects.'.format(data)) - - def encode_json(self, data: dict, pretty: bool=False) -> str: - """Orders a python dict recursively so it will always hash to the - same value. Used for Dedupping harvest results Args: - data (dict): + start (date): + end (date): + limit (int, optional): The maximum number of unique data to harvest. Defaults to None. + Uniqueness is determined by the SHA-256 of the raw data + force (bool, optional): Disable all safety checks, unexpected exceptions will still be raised. Defaults to False. + ignore_disabled (bool, optional): Don't check if this Harvester or Source is disabled or deleted. Defaults to False. + **kwargs: Forwared to _do_fetch. + + Yields: + RawDatum - Returns: - str: json.dumpsed ordered dictionary """ - return json.dumps(data, sort_keys=True, indent=4 if pretty else None) + if self.serializer.pretty: + raise ValueError('To ensure that data is optimally deduplicated, harvests may not occur while using a pretty serializer.') - def _validate_dates(self, start_date, end_date): - assert not (bool(start_date) ^ bool(end_date)), 'Must specify both a start and end date or neither' - assert isinstance(start_date, (datetime.timedelta, datetime.datetime, pendulum.Pendulum)) and isinstance(end_date, (datetime.timedelta, datetime.datetime, pendulum.Pendulum)), 'start_date and end_date must be either datetimes or timedeltas' - assert not (isinstance(start_date, datetime.timedelta) and isinstance(end_date, datetime.timedelta)), 'Only one of start_date and end_date may be a timedelta' + if (self.config.disabled or self.config.source.is_deleted) and not (force or ignore_disabled): + raise HarvesterDisabledError('Harvester {!r} is disabled. Either enable it, run with force=True, or ignore_disabled=True.'.format(self.config)) - if isinstance(start_date, datetime.datetime): - start_date = pendulum.instance(start_date) + with self.config.acquire_lock(required=not force): + logger.info('Harvesting %s - %s from %r', start, end, self.config) + yield from RawDatum.objects.store_chunk(self.config, self.fetch_date_range(start, end, **kwargs), limit=limit) - if isinstance(end_date, datetime.datetime): - end_date = pendulum.instance(end_date) + def harvest_from_log(self, harvest_log, **kwargs): + """Harvest data as specified by the given harvest_log. - if isinstance(start_date, datetime.timedelta): - start_date = pendulum.instance(end_date + start_date) + Args: + harvest_log (HarvestLog): The HarvestLog that describes the parameters of this harvest + limit (int, optional): The maximum number of unique data to harvest. Defaults to None. + **kwargs: Forwared to harvest_date_range. - if isinstance(end_date, datetime.timedelta): - end_date = pendulum.instance(start_date + end_date) + Yields: + RawDatum - og_start, og_end = start_date, end_date - start_date, end_date = self.shift_range(start_date, end_date) - assert isinstance(start_date, pendulum.Pendulum) and isinstance(end_date, pendulum.Pendulum), 'transpose_time_window must return a tuple of 2 datetimes' + """ + error = None + datum_ids = [] + logger.info('Harvesting %r', harvest_log) + + with harvest_log.handle(self.VERSION): + try: + for datum in self.harvest_date_range(harvest_log.start_date, harvest_log.end_date, **kwargs): + datum_ids.append(datum.id) + yield datum + except Exception as e: + error = e + raise error + finally: + try: + harvest_log.raw_data.add(*datum_ids) + except Exception as e: + logger.exception('Failed to connection %r to raw data', harvest_log) + # Avoid shadowing the original error + if not error: + raise e + + def _do_fetch(self, start, end, **kwargs): + """Fetch date from this source inside of the given date range. + + The given date range should be treated as [start, end) - if (og_start, og_end) != (start_date, end_date): - logger.warning('Date shifted from {} - {} to {} - {}. Disable shifting by passing shift_range=False'.format(og_start, og_end, start_date, end_date)) + Any HTTP[S] requests MUST be sent using the self.requests client. + It will automatically enforce rate limits - assert start_date < end_date, 'start_date must be before end_date {} < {}'.format(start_date, end_date) + Args: + start_date (datetime): Date to start fetching data from, inclusively. + end_date (datetime): Date to fetch data up to, exclusively. + **kwargs: Arbitrary kwargs passed to subclasses, used to customize harvesting. + + Returns: + Iterator: The fetched data. - return start_date, end_date + """ + if hasattr(self, 'do_harvest'): + warnings.warn( + '{!r} implements a deprecated interface. ' + 'do_harvest has been replaced by _do_fetch for clarity. ' + 'do_harvest will no longer be called in SHARE 2.11.0'.format(self), + DeprecationWarning + ) + logger.warning('%r implements a deprecated interface. ', self) + return self.do_harvest(start, end, **kwargs) + + raise NotImplementedError() From 162b9777a0cb22d7c2333bf448d0658a8fdf7e82 Mon Sep 17 00:00:00 2001 From: Emily Carden Date: Mon, 19 Jun 2017 14:43:37 -0400 Subject: [PATCH 14/34] display info from source config api on the harvest log page --- api/serializers.py | 2 +- api/views/harvestlogs.py | 7 +++---- api/views/sourceConfig.py | 8 ++++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index 81edcae9f..a1fa43d44 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -5,7 +5,7 @@ from rest_framework_json_api import serializers from share import models -from share.models import ProviderRegistration, SiteBanner, CeleryTaskResult, HarvestLog +from share.models import ProviderRegistration, SiteBanner, CeleryTaskResult, HarvestLog, SourceConfig from api import fields diff --git a/api/views/harvestlogs.py b/api/views/harvestlogs.py index 79b68b6e5..7401ba970 100644 --- a/api/views/harvestlogs.py +++ b/api/views/harvestlogs.py @@ -1,10 +1,9 @@ from rest_framework import viewsets +from api.views import ShareObjectViewSet from api.serializers import HarvestLogSerializer from share.models import HarvestLog -class HarvestLogViewSet(viewsets.ReadOnlyModelViewSet): +class HarvestLogViewSet(ShareObjectViewSet): serializer_class = HarvestLogSerializer - - def get_queryset(self): - return HarvestLog.objects.all() + queryset = HarvestLog.objects.all() diff --git a/api/views/sourceConfig.py b/api/views/sourceConfig.py index ecc76f054..c287701ef 100644 --- a/api/views/sourceConfig.py +++ b/api/views/sourceConfig.py @@ -1,10 +1,10 @@ from rest_framework import viewsets +from api.views import ShareObjectViewSet + # trying to fix shit from api.serializers import SourceConfigSerializer from share.models import SourceConfig -class SourceConfigViewSet(viewsets.ReadOnlyModelViewSet): +class SourceConfigViewSet(ShareObjectViewSet): serializer_class = SourceConfigSerializer - - def get_queryset(self): - return SourceConfig.objects.all() + queryset= SourceConfig.objects.all() From 6b7ac4f5ef8602dcb34691029580528b89d7b11c Mon Sep 17 00:00:00 2001 From: Emily Carden Date: Wed, 21 Jun 2017 14:55:22 -0400 Subject: [PATCH 15/34] new backend filter for source config id's --- api/views/harvestlogs.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/api/views/harvestlogs.py b/api/views/harvestlogs.py index 7401ba970..02fa70b62 100644 --- a/api/views/harvestlogs.py +++ b/api/views/harvestlogs.py @@ -1,9 +1,24 @@ from rest_framework import viewsets +from rest_framework import filters from api.views import ShareObjectViewSet +from share.util import IDObfuscator from api.serializers import HarvestLogSerializer from share.models import HarvestLog + +class SourceConfigFilterBackend(filters.BaseFilterBackend): + """ + Filter that only allows users to see their own objects. + """ + def filter_queryset(self, request, queryset, view): + if 'source_config_id' in request.GET: + decoded = IDObfuscator.decode_id(request.GET['source_config_id']) + return queryset.filter(source_config_id=decoded) + else: + return queryset + class HarvestLogViewSet(ShareObjectViewSet): serializer_class = HarvestLogSerializer queryset = HarvestLog.objects.all() + filter_backends = (SourceConfigFilterBackend, ) From f61ea16aaa427a176abc391bbca393dd54bad601 Mon Sep 17 00:00:00 2001 From: Oludare Oludare Date: Wed, 21 Jun 2017 15:53:49 -0400 Subject: [PATCH 16/34] changes --- api/views/harvestlogs.py | 16 +++++++++++----- api/views/share.py | 2 ++ api/views/sourceConfig.py | 15 +++++++++------ docs/elasticsearch.rst | 1 + 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/api/views/harvestlogs.py b/api/views/harvestlogs.py index 79b68b6e5..49ef44eda 100644 --- a/api/views/harvestlogs.py +++ b/api/views/harvestlogs.py @@ -1,10 +1,16 @@ -from rest_framework import viewsets - +from rest_framework import viewsets, views +# from rest_framework import filters +from django_filters.rest_framework import DjangoFilterBackend from api.serializers import HarvestLogSerializer from share.models import HarvestLog +from api.views import ShareObjectViewSet -class HarvestLogViewSet(viewsets.ReadOnlyModelViewSet): +class HarvestLogViewSet(ShareObjectViewSet): + queryset= HarvestLog.objects.all() serializer_class = HarvestLogSerializer - def get_queryset(self): - return HarvestLog.objects.all() + filter_backends = (DjangoFilterBackend, ) + filter_fields = ['source_config__id',] + + # def get_queryset(self): + # import ipdb; ipdb.set_trace() diff --git a/api/views/share.py b/api/views/share.py index 798762c2a..3a0ed5248 100644 --- a/api/views/share.py +++ b/api/views/share.py @@ -28,6 +28,7 @@ class ShareObjectViewSet(viewsets.ReadOnlyModelViewSet): # Override get_queryset to handle items marked as deleted. def get_queryset(self, list=True): + # import ipdb; ipdb.set_trace() queryset = super().get_queryset() if list and hasattr(queryset.model, 'is_deleted'): return queryset.exclude(is_deleted=True) @@ -35,6 +36,7 @@ def get_queryset(self, list=True): # Override to convert encoded pk to an actual pk def get_object(self): + import ipdb; ipdb.set_trace() queryset = self.filter_queryset(self.get_queryset(False)) # Perform the lookup filtering. diff --git a/api/views/sourceConfig.py b/api/views/sourceConfig.py index ecc76f054..9861aba22 100644 --- a/api/views/sourceConfig.py +++ b/api/views/sourceConfig.py @@ -1,10 +1,13 @@ -from rest_framework import viewsets -# trying to fix shit +from rest_framework import viewsets, views from api.serializers import SourceConfigSerializer from share.models import SourceConfig +from rest_framework import filters +import django_filters.rest_framework +from api.views import ShareObjectViewSet -class SourceConfigViewSet(viewsets.ReadOnlyModelViewSet): - serializer_class = SourceConfigSerializer - def get_queryset(self): - return SourceConfig.objects.all() +class SourceConfigViewSet(ShareObjectViewSet): + queryset= SourceConfig.objects.all() + serializer_class = SourceConfigSerializer + filter_backends = (filters.SearchFilter, ) + search_fields = ['label','base_url'] diff --git a/docs/elasticsearch.rst b/docs/elasticsearch.rst index 5c6f59b37..3d0645b25 100644 --- a/docs/elasticsearch.rst +++ b/docs/elasticsearch.rst @@ -26,6 +26,7 @@ Elasticsearch can be used to search the following fields in the normalized data: 'contributors' 'funders' 'publishers' + 'id' Accessing the Search API From e1182a9fd6da166b2888dc6e5e37f773d170a48b Mon Sep 17 00:00:00 2001 From: Oludare Olugbemi Date: Thu, 22 Jun 2017 16:22:49 -0400 Subject: [PATCH 17/34] no message --- bin/activate | 76 + bin/activate.csh | 37 + bin/activate.fish | 75 + bin/celery | 12 + bin/createfontdatachunk.py | 16 + bin/django-admin | 11 + bin/django-admin.py | 5 + bin/easy_install | 11 + bin/easy_install-3.5 | 11 + bin/enhancer.py | 63 + bin/explode.py | 112 ++ bin/gifmaker.py | 31 + bin/jp.py | 54 + bin/jsonschema | 11 + bin/markdown_py | 34 + bin/newrelic-admin | 12 + bin/painter.py | 82 + bin/pbr | 11 + bin/pilconvert.py | 99 ++ bin/pildriver.py | 526 +++++++ bin/pilfile.py | 101 ++ bin/pilfont.py | 57 + bin/pilprint.py | 102 ++ bin/pip | 11 + bin/pip3 | 11 + bin/pip3.5 | 11 + bin/player.py | 102 ++ bin/python | 1 + bin/python3 | 1 + bin/python3.5 | 1 + bin/raven | 11 + bin/rst2html.py | 23 + bin/rst2html5.py | 35 + bin/rst2latex.py | 26 + bin/rst2man.py | 26 + bin/rst2odt.py | 30 + bin/rst2odt_prepstyles.py | 67 + bin/rst2pseudoxml.py | 23 + bin/rst2s5.py | 24 + bin/rst2xetex.py | 27 + bin/rst2xml.py | 23 + bin/rstpep2html.py | 25 + bin/sharectl | 12 + bin/thresholder.py | 78 + bin/viewer.py | 54 + include/site/python3.5/greenlet/greenlet.h | 148 ++ pip-selfcheck.json | 1 + pyvenv.cfg | 3 + share/doc/networkx-1.11/INSTALL.txt | 3 + share/doc/networkx-1.11/LICENSE.txt | 40 + .../examples/3d_drawing/mayavi2_spring.py | 37 + .../examples/advanced/eigenvalues.py | 20 + .../examples/advanced/heavy_metal_umlaut.py | 77 + .../advanced/iterated_dynamical_systems.py | 199 +++ .../examples/advanced/parallel_betweenness.py | 73 + .../examples/algorithms/blockmodel.py | 83 + .../examples/algorithms/davis_club.py | 36 + .../algorithms/hartford_drug.edgelist | 338 ++++ .../algorithms/krackhardt_centrality.py | 33 + .../networkx-1.11/examples/algorithms/rcm.py | 32 + .../examples/basic/properties.py | 48 + .../examples/basic/read_write.py | 25 + .../networkx-1.11/examples/drawing/atlas.py | 89 ++ .../examples/drawing/chess_masters.py | 162 ++ .../drawing/chess_masters_WCC.pgn.bz2 | Bin 0 -> 100224 bytes .../examples/drawing/circular_tree.py | 22 + .../examples/drawing/degree_histogram.py | 31 + .../examples/drawing/edge_colormap.py | 19 + .../examples/drawing/ego_graph.py | 30 + .../examples/drawing/four_grids.py | 40 + .../examples/drawing/giant_component.py | 79 + .../examples/drawing/house_with_colors.py | 27 + .../examples/drawing/knuth_miles.py | 112 ++ .../examples/drawing/knuth_miles.txt.gz | Bin 0 -> 20317 bytes .../examples/drawing/labels_and_colors.py | 51 + .../examples/drawing/lanl_routes.edgelist | 1363 +++++++++++++++++ .../examples/drawing/lanl_routes.py | 80 + .../examples/drawing/node_colormap.py | 19 + .../drawing/random_geometric_graph.py | 33 + .../networkx-1.11/examples/drawing/sampson.py | 46 + .../examples/drawing/simple_path.py | 16 + .../examples/drawing/unix_email.mbox | 84 + .../examples/drawing/unix_email.py | 85 + .../examples/drawing/weighted_graph.py | 41 + .../doc/networkx-1.11/examples/graph/atlas.py | 89 ++ .../networkx-1.11/examples/graph/atlas2.py | 33 + .../examples/graph/degree_sequence.py | 34 + .../examples/graph/erdos_renyi.py | 38 + .../graph/expected_degree_sequence.py | 29 + .../networkx-1.11/examples/graph/football.py | 46 + .../examples/graph/karate_club.py | 17 + .../examples/graph/knuth_miles.py | 112 ++ .../examples/graph/knuth_miles.txt.gz | Bin 0 -> 20317 bytes .../graph/napoleon_russian_campaign.py | 145 ++ .../doc/networkx-1.11/examples/graph/roget.py | 81 + .../examples/graph/roget_dat.txt.gz | Bin 0 -> 15758 bytes .../examples/graph/unix_email.mbox | 84 + .../examples/graph/unix_email.py | 85 + .../doc/networkx-1.11/examples/graph/words.py | 84 + .../examples/graph/words_dat.txt.gz | Bin 0 -> 33695 bytes .../examples/multigraph/chess_masters.py | 162 ++ .../multigraph/chess_masters_WCC.pgn.bz2 | Bin 0 -> 100224 bytes .../pygraphviz/pygraphviz_attributes.py | 44 + .../examples/pygraphviz/pygraphviz_draw.py | 28 + .../examples/pygraphviz/pygraphviz_simple.py | 32 + .../examples/pygraphviz/write_dotfile.py | 44 + 106 files changed, 6983 insertions(+) create mode 100644 bin/activate create mode 100644 bin/activate.csh create mode 100644 bin/activate.fish create mode 100755 bin/celery create mode 100755 bin/createfontdatachunk.py create mode 100755 bin/django-admin create mode 100755 bin/django-admin.py create mode 100755 bin/easy_install create mode 100755 bin/easy_install-3.5 create mode 100755 bin/enhancer.py create mode 100755 bin/explode.py create mode 100755 bin/gifmaker.py create mode 100755 bin/jp.py create mode 100755 bin/jsonschema create mode 100755 bin/markdown_py create mode 100755 bin/newrelic-admin create mode 100755 bin/painter.py create mode 100755 bin/pbr create mode 100755 bin/pilconvert.py create mode 100755 bin/pildriver.py create mode 100755 bin/pilfile.py create mode 100755 bin/pilfont.py create mode 100755 bin/pilprint.py create mode 100755 bin/pip create mode 100755 bin/pip3 create mode 100755 bin/pip3.5 create mode 100755 bin/player.py create mode 120000 bin/python create mode 120000 bin/python3 create mode 120000 bin/python3.5 create mode 100755 bin/raven create mode 100755 bin/rst2html.py create mode 100755 bin/rst2html5.py create mode 100755 bin/rst2latex.py create mode 100755 bin/rst2man.py create mode 100755 bin/rst2odt.py create mode 100755 bin/rst2odt_prepstyles.py create mode 100755 bin/rst2pseudoxml.py create mode 100755 bin/rst2s5.py create mode 100755 bin/rst2xetex.py create mode 100755 bin/rst2xml.py create mode 100755 bin/rstpep2html.py create mode 100755 bin/sharectl create mode 100755 bin/thresholder.py create mode 100755 bin/viewer.py create mode 100644 include/site/python3.5/greenlet/greenlet.h create mode 100644 pip-selfcheck.json create mode 100644 pyvenv.cfg create mode 100644 share/doc/networkx-1.11/INSTALL.txt create mode 100644 share/doc/networkx-1.11/LICENSE.txt create mode 100644 share/doc/networkx-1.11/examples/3d_drawing/mayavi2_spring.py create mode 100644 share/doc/networkx-1.11/examples/advanced/eigenvalues.py create mode 100644 share/doc/networkx-1.11/examples/advanced/heavy_metal_umlaut.py create mode 100644 share/doc/networkx-1.11/examples/advanced/iterated_dynamical_systems.py create mode 100644 share/doc/networkx-1.11/examples/advanced/parallel_betweenness.py create mode 100644 share/doc/networkx-1.11/examples/algorithms/blockmodel.py create mode 100644 share/doc/networkx-1.11/examples/algorithms/davis_club.py create mode 100644 share/doc/networkx-1.11/examples/algorithms/hartford_drug.edgelist create mode 100644 share/doc/networkx-1.11/examples/algorithms/krackhardt_centrality.py create mode 100644 share/doc/networkx-1.11/examples/algorithms/rcm.py create mode 100644 share/doc/networkx-1.11/examples/basic/properties.py create mode 100644 share/doc/networkx-1.11/examples/basic/read_write.py create mode 100644 share/doc/networkx-1.11/examples/drawing/atlas.py create mode 100644 share/doc/networkx-1.11/examples/drawing/chess_masters.py create mode 100644 share/doc/networkx-1.11/examples/drawing/chess_masters_WCC.pgn.bz2 create mode 100644 share/doc/networkx-1.11/examples/drawing/circular_tree.py create mode 100644 share/doc/networkx-1.11/examples/drawing/degree_histogram.py create mode 100644 share/doc/networkx-1.11/examples/drawing/edge_colormap.py create mode 100644 share/doc/networkx-1.11/examples/drawing/ego_graph.py create mode 100644 share/doc/networkx-1.11/examples/drawing/four_grids.py create mode 100644 share/doc/networkx-1.11/examples/drawing/giant_component.py create mode 100644 share/doc/networkx-1.11/examples/drawing/house_with_colors.py create mode 100644 share/doc/networkx-1.11/examples/drawing/knuth_miles.py create mode 100644 share/doc/networkx-1.11/examples/drawing/knuth_miles.txt.gz create mode 100644 share/doc/networkx-1.11/examples/drawing/labels_and_colors.py create mode 100644 share/doc/networkx-1.11/examples/drawing/lanl_routes.edgelist create mode 100644 share/doc/networkx-1.11/examples/drawing/lanl_routes.py create mode 100644 share/doc/networkx-1.11/examples/drawing/node_colormap.py create mode 100644 share/doc/networkx-1.11/examples/drawing/random_geometric_graph.py create mode 100644 share/doc/networkx-1.11/examples/drawing/sampson.py create mode 100644 share/doc/networkx-1.11/examples/drawing/simple_path.py create mode 100644 share/doc/networkx-1.11/examples/drawing/unix_email.mbox create mode 100755 share/doc/networkx-1.11/examples/drawing/unix_email.py create mode 100644 share/doc/networkx-1.11/examples/drawing/weighted_graph.py create mode 100644 share/doc/networkx-1.11/examples/graph/atlas.py create mode 100644 share/doc/networkx-1.11/examples/graph/atlas2.py create mode 100644 share/doc/networkx-1.11/examples/graph/degree_sequence.py create mode 100644 share/doc/networkx-1.11/examples/graph/erdos_renyi.py create mode 100644 share/doc/networkx-1.11/examples/graph/expected_degree_sequence.py create mode 100644 share/doc/networkx-1.11/examples/graph/football.py create mode 100644 share/doc/networkx-1.11/examples/graph/karate_club.py create mode 100644 share/doc/networkx-1.11/examples/graph/knuth_miles.py create mode 100644 share/doc/networkx-1.11/examples/graph/knuth_miles.txt.gz create mode 100644 share/doc/networkx-1.11/examples/graph/napoleon_russian_campaign.py create mode 100644 share/doc/networkx-1.11/examples/graph/roget.py create mode 100644 share/doc/networkx-1.11/examples/graph/roget_dat.txt.gz create mode 100644 share/doc/networkx-1.11/examples/graph/unix_email.mbox create mode 100644 share/doc/networkx-1.11/examples/graph/unix_email.py create mode 100644 share/doc/networkx-1.11/examples/graph/words.py create mode 100644 share/doc/networkx-1.11/examples/graph/words_dat.txt.gz create mode 100644 share/doc/networkx-1.11/examples/multigraph/chess_masters.py create mode 100644 share/doc/networkx-1.11/examples/multigraph/chess_masters_WCC.pgn.bz2 create mode 100644 share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_attributes.py create mode 100644 share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_draw.py create mode 100644 share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_simple.py create mode 100644 share/doc/networkx-1.11/examples/pygraphviz/write_dotfile.py diff --git a/bin/activate b/bin/activate new file mode 100644 index 000000000..9cbc0a3f9 --- /dev/null +++ b/bin/activate @@ -0,0 +1,76 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "$_OLD_VIRTUAL_PATH" ] ; then + PATH="$_OLD_VIRTUAL_PATH" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "$_OLD_VIRTUAL_PYTHONHOME" ] ; then + PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then + hash -r + fi + + if [ -n "$_OLD_VIRTUAL_PS1" ] ; then + PS1="$_OLD_VIRTUAL_PS1" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + if [ ! "$1" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="/Users/admin/Share" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "$PYTHONHOME" ] ; then + _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME" + unset PYTHONHOME +fi + +if [ -z "$VIRTUAL_ENV_DISABLE_PROMPT" ] ; then + _OLD_VIRTUAL_PS1="$PS1" + if [ "x(Share) " != x ] ; then + PS1="(Share) $PS1" + else + if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1" + else + PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1" + fi + fi + export PS1 +fi + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then + hash -r +fi diff --git a/bin/activate.csh b/bin/activate.csh new file mode 100644 index 000000000..da83767ff --- /dev/null +++ b/bin/activate.csh @@ -0,0 +1,37 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/Users/admin/Share" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + if ("Share" != "") then + set env_name = "Share" + else + if (`basename "VIRTUAL_ENV"` == "__") then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + set env_name = `basename \`dirname "$VIRTUAL_ENV"\`` + else + set env_name = `basename "$VIRTUAL_ENV"` + endif + endif + set prompt = "[$env_name] $prompt" + unset env_name +endif + +alias pydoc python -m pydoc + +rehash diff --git a/bin/activate.fish b/bin/activate.fish new file mode 100644 index 000000000..aadca2db7 --- /dev/null +++ b/bin/activate.fish @@ -0,0 +1,75 @@ +# This file must be used with ". bin/activate.fish" *from fish* (http://fishshell.org) +# you cannot run it directly + +function deactivate -d "Exit virtualenv and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + functions -e fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + + set -e VIRTUAL_ENV + if test "$argv[1]" != "nondestructive" + # Self destruct! + functions -e deactivate + end +end + +# unset irrelevant variables +deactivate nondestructive + +set -gx VIRTUAL_ENV "/Users/admin/Share" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# unset PYTHONHOME if set +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # save the current fish_prompt function as the function _old_fish_prompt + functions -c fish_prompt _old_fish_prompt + + # with the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command + set -l old_status $status + + # Prompt override? + if test -n "(Share) " + printf "%s%s" "(Share) " (set_color normal) + else + # ...Otherwise, prepend env + set -l _checkbase (basename "$VIRTUAL_ENV") + if test $_checkbase = "__" + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + printf "%s[%s]%s " (set_color -b blue white) (basename (dirname "$VIRTUAL_ENV")) (set_color normal) + else + printf "%s(%s)%s" (set_color -b blue white) (basename "$VIRTUAL_ENV") (set_color normal) + end + end + + # Restore the return status of the previous command. + echo "exit $old_status" | . + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end diff --git a/bin/celery b/bin/celery new file mode 100755 index 000000000..96c8eea01 --- /dev/null +++ b/bin/celery @@ -0,0 +1,12 @@ +#!/Users/admin/SHARE/bin/python3.5 +# EASY-INSTALL-ENTRY-SCRIPT: 'celery==4.0.2','console_scripts','celery' +__requires__ = 'celery==4.0.2' +import re +import sys +from pkg_resources import load_entry_point + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit( + load_entry_point('celery==4.0.2', 'console_scripts', 'celery')() + ) diff --git a/bin/createfontdatachunk.py b/bin/createfontdatachunk.py new file mode 100755 index 000000000..ea664f23c --- /dev/null +++ b/bin/createfontdatachunk.py @@ -0,0 +1,16 @@ +#!/Users/admin/SHARE/bin/python3.5 +from __future__ import print_function +import base64 +import os +import sys + +if __name__ == "__main__": + # create font data chunk for embedding + font = "Tests/images/courB08" + print(" f._load_pilfont_data(") + print(" # %s" % os.path.basename(font)) + print(" BytesIO(base64.decodestring(b'''") + base64.encode(open(font + ".pil", "rb"), sys.stdout) + print("''')), Image.open(BytesIO(base64.decodestring(b'''") + base64.encode(open(font + ".pbm", "rb"), sys.stdout) + print("'''))))") diff --git a/bin/django-admin b/bin/django-admin new file mode 100755 index 000000000..19bc5157b --- /dev/null +++ b/bin/django-admin @@ -0,0 +1,11 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from django.core.management import execute_from_command_line + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(execute_from_command_line()) diff --git a/bin/django-admin.py b/bin/django-admin.py new file mode 100755 index 000000000..dfcc1386f --- /dev/null +++ b/bin/django-admin.py @@ -0,0 +1,5 @@ +#!/Users/admin/SHARE/bin/python3.5 +from django.core import management + +if __name__ == "__main__": + management.execute_from_command_line() diff --git a/bin/easy_install b/bin/easy_install new file mode 100755 index 000000000..be946e242 --- /dev/null +++ b/bin/easy_install @@ -0,0 +1,11 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from setuptools.command.easy_install import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bin/easy_install-3.5 b/bin/easy_install-3.5 new file mode 100755 index 000000000..be946e242 --- /dev/null +++ b/bin/easy_install-3.5 @@ -0,0 +1,11 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from setuptools.command.easy_install import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bin/enhancer.py b/bin/enhancer.py new file mode 100755 index 000000000..8f9e4f76c --- /dev/null +++ b/bin/enhancer.py @@ -0,0 +1,63 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library +# $Id$ +# +# this demo script creates four windows containing an image and a slider. +# drag the slider to modify the image. +# + +try: + from tkinter import Tk, Toplevel, Frame, Label, Scale, HORIZONTAL +except ImportError: + from Tkinter import Tk, Toplevel, Frame, Label, Scale, HORIZONTAL + +from PIL import Image, ImageTk, ImageEnhance +import sys + +# +# enhancer widget + + +class Enhance(Frame): + def __init__(self, master, image, name, enhancer, lo, hi): + Frame.__init__(self, master) + + # set up the image + self.tkim = ImageTk.PhotoImage(image.mode, image.size) + self.enhancer = enhancer(image) + self.update("1.0") # normalize + + # image window + Label(self, image=self.tkim).pack() + + # scale + s = Scale(self, label=name, orient=HORIZONTAL, + from_=lo, to=hi, resolution=0.01, + command=self.update) + s.set(self.value) + s.pack() + + def update(self, value): + self.value = float(value) + self.tkim.paste(self.enhancer.enhance(self.value)) + +# +# main + +if len(sys.argv) != 2: + print("Usage: enhancer file") + sys.exit(1) + +root = Tk() + +im = Image.open(sys.argv[1]) + +im.thumbnail((200, 200)) + +Enhance(root, im, "Color", ImageEnhance.Color, 0.0, 4.0).pack() +Enhance(Toplevel(), im, "Sharpness", ImageEnhance.Sharpness, -2.0, 2.0).pack() +Enhance(Toplevel(), im, "Brightness", ImageEnhance.Brightness, -1.0, 3.0).pack() +Enhance(Toplevel(), im, "Contrast", ImageEnhance.Contrast, -1.0, 3.0).pack() + +root.mainloop() diff --git a/bin/explode.py b/bin/explode.py new file mode 100755 index 000000000..aec0a5cd5 --- /dev/null +++ b/bin/explode.py @@ -0,0 +1,112 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library +# $Id$ +# +# split an animation into a number of frame files +# + +from __future__ import print_function + +from PIL import Image +import os +import sys + + +class Interval(object): + + def __init__(self, interval="0"): + + self.setinterval(interval) + + def setinterval(self, interval): + + self.hilo = [] + + for s in interval.split(","): + if not s.strip(): + continue + try: + v = int(s) + if v < 0: + lo, hi = 0, -v + else: + lo = hi = v + except ValueError: + i = s.find("-") + lo, hi = int(s[:i]), int(s[i+1:]) + + self.hilo.append((hi, lo)) + + if not self.hilo: + self.hilo = [(sys.maxsize, 0)] + + def __getitem__(self, index): + + for hi, lo in self.hilo: + if hi >= index >= lo: + return 1 + return 0 + +# -------------------------------------------------------------------- +# main program + +html = 0 + +if sys.argv[1:2] == ["-h"]: + html = 1 + del sys.argv[1] + +if not sys.argv[2:]: + print() + print("Syntax: python explode.py infile template [range]") + print() + print("The template argument is used to construct the names of the") + print("individual frame files. The frames are numbered file001.ext,") + print("file002.ext, etc. You can insert %d to control the placement") + print("and syntax of the frame number.") + print() + print("The optional range argument specifies which frames to extract.") + print("You can give one or more ranges like 1-10, 5, -15 etc. If") + print("omitted, all frames are extracted.") + sys.exit(1) + +infile = sys.argv[1] +outfile = sys.argv[2] + +frames = Interval(",".join(sys.argv[3:])) + +try: + # check if outfile contains a placeholder + outfile % 1 +except TypeError: + file, ext = os.path.splitext(outfile) + outfile = file + "%03d" + ext + +ix = 1 + +im = Image.open(infile) + +if html: + file, ext = os.path.splitext(outfile) + html = open(file+".html", "w") + html.write("\n\n") + +while True: + + if frames[ix]: + im.save(outfile % ix) + print(outfile % ix) + + if html: + html.write("
\n" % outfile % ix) + + try: + im.seek(ix) + except EOFError: + break + + ix += 1 + +if html: + html.write("\n\n") diff --git a/bin/gifmaker.py b/bin/gifmaker.py new file mode 100755 index 000000000..74881e460 --- /dev/null +++ b/bin/gifmaker.py @@ -0,0 +1,31 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library +# $Id$ +# +# convert sequence format to GIF animation +# +# history: +# 97-01-03 fl created +# +# Copyright (c) Secret Labs AB 1997. All rights reserved. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +from __future__ import print_function + +from PIL import Image + +if __name__ == "__main__": + + import sys + + if len(sys.argv) < 3: + print("GIFMAKER -- create GIF animations") + print("Usage: gifmaker infile outfile") + sys.exit(1) + + im = Image.open(sys.argv[1]) + im.save(sys.argv[2], save_all=True) diff --git a/bin/jp.py b/bin/jp.py new file mode 100755 index 000000000..82779072d --- /dev/null +++ b/bin/jp.py @@ -0,0 +1,54 @@ +#!/Users/admin/SHARE/bin/python3.5 + +import sys +import json +import argparse +from pprint import pformat + +import jmespath +from jmespath import exceptions + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('expression') + parser.add_argument('-f', '--filename', + help=('The filename containing the input data. ' + 'If a filename is not given then data is ' + 'read from stdin.')) + parser.add_argument('--ast', action='store_true', + help=('Pretty print the AST, do not search the data.')) + args = parser.parse_args() + expression = args.expression + if args.ast: + # Only print the AST + expression = jmespath.compile(args.expression) + sys.stdout.write(pformat(expression.parsed)) + sys.stdout.write('\n') + return 0 + if args.filename: + with open(args.filename, 'r') as f: + data = json.load(f) + else: + data = sys.stdin.read() + data = json.loads(data) + try: + sys.stdout.write(json.dumps( + jmespath.search(expression, data), indent=4)) + sys.stdout.write('\n') + except exceptions.ArityError as e: + sys.stderr.write("invalid-arity: %s\n" % e) + return 1 + except exceptions.JMESPathTypeError as e: + sys.stderr.write("invalid-type: %s\n" % e) + return 1 + except exceptions.UnknownFunctionError as e: + sys.stderr.write("unknown-function: %s\n" % e) + return 1 + except exceptions.ParseError as e: + sys.stderr.write("syntax-error: %s\n" % e) + return 1 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/bin/jsonschema b/bin/jsonschema new file mode 100755 index 000000000..525169637 --- /dev/null +++ b/bin/jsonschema @@ -0,0 +1,11 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from jsonschema.cli import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bin/markdown_py b/bin/markdown_py new file mode 100755 index 000000000..7763009b9 --- /dev/null +++ b/bin/markdown_py @@ -0,0 +1,34 @@ +#!/Users/admin/SHARE/bin/python3.5 +""" +Python Markdown, the Command Line Script +======================================== + +This is the command line script for Python Markdown. + +Basic use from the command line: + + markdown source.txt > destination.html + +Run "markdown --help" to see more options. + +See markdown/__init__.py for information on using Python Markdown as a module. + +## Authors and License + +Started by [Manfred Stienstra](http://www.dwerg.net/). Continued and +maintained by [Yuri Takhteyev](http://www.freewisdom.org), [Waylan +Limberg](http://achinghead.com/) and [Artem Yunusov](http://blog.splyer.com). + +Contact: markdown@freewisdom.org + +Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later) +Copyright 200? Django Software Foundation (OrderedDict implementation) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see docs/LICENSE for details). +""" + +if __name__ == '__main__': + from markdown.__main__ import run + run() diff --git a/bin/newrelic-admin b/bin/newrelic-admin new file mode 100755 index 000000000..91923b908 --- /dev/null +++ b/bin/newrelic-admin @@ -0,0 +1,12 @@ +#!/Users/admin/SHARE/bin/python3.5 +# EASY-INSTALL-ENTRY-SCRIPT: 'newrelic==2.68.0.50','console_scripts','newrelic-admin' +__requires__ = 'newrelic==2.68.0.50' +import re +import sys +from pkg_resources import load_entry_point + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit( + load_entry_point('newrelic==2.68.0.50', 'console_scripts', 'newrelic-admin')() + ) diff --git a/bin/painter.py b/bin/painter.py new file mode 100755 index 000000000..f4d33f18c --- /dev/null +++ b/bin/painter.py @@ -0,0 +1,82 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library +# $Id$ +# +# this demo script illustrates pasting into an already displayed +# photoimage. note that the current version of Tk updates the whole +# image every time we paste, so to get decent performance, we split +# the image into a set of tiles. +# + +try: + from tkinter import Tk, Canvas, NW +except ImportError: + from Tkinter import Tk, Canvas, NW + +from PIL import Image, ImageTk +import sys + +# +# painter widget + + +class PaintCanvas(Canvas): + def __init__(self, master, image): + Canvas.__init__(self, master, width=image.size[0], height=image.size[1]) + + # fill the canvas + self.tile = {} + self.tilesize = tilesize = 32 + xsize, ysize = image.size + for x in range(0, xsize, tilesize): + for y in range(0, ysize, tilesize): + box = x, y, min(xsize, x+tilesize), min(ysize, y+tilesize) + tile = ImageTk.PhotoImage(image.crop(box)) + self.create_image(x, y, image=tile, anchor=NW) + self.tile[(x, y)] = box, tile + + self.image = image + + self.bind("", self.paint) + + def paint(self, event): + xy = event.x - 10, event.y - 10, event.x + 10, event.y + 10 + im = self.image.crop(xy) + + # process the image in some fashion + im = im.convert("L") + + self.image.paste(im, xy) + self.repair(xy) + + def repair(self, box): + # update canvas + dx = box[0] % self.tilesize + dy = box[1] % self.tilesize + for x in range(box[0]-dx, box[2]+1, self.tilesize): + for y in range(box[1]-dy, box[3]+1, self.tilesize): + try: + xy, tile = self.tile[(x, y)] + tile.paste(self.image.crop(xy)) + except KeyError: + pass # outside the image + self.update_idletasks() + +# +# main + +if len(sys.argv) != 2: + print("Usage: painter file") + sys.exit(1) + +root = Tk() + +im = Image.open(sys.argv[1]) + +if im.mode != "RGB": + im = im.convert("RGB") + +PaintCanvas(root, im).pack() + +root.mainloop() diff --git a/bin/pbr b/bin/pbr new file mode 100755 index 000000000..2a86bdeeb --- /dev/null +++ b/bin/pbr @@ -0,0 +1,11 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from pbr.cmd.main import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bin/pilconvert.py b/bin/pilconvert.py new file mode 100755 index 000000000..3135eb9c4 --- /dev/null +++ b/bin/pilconvert.py @@ -0,0 +1,99 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library. +# $Id$ +# +# convert image files +# +# History: +# 0.1 96-04-20 fl Created +# 0.2 96-10-04 fl Use draft mode when converting images +# 0.3 96-12-30 fl Optimize output (PNG, JPEG) +# 0.4 97-01-18 fl Made optimize an option (PNG, JPEG) +# 0.5 98-12-30 fl Fixed -f option (from Anthony Baxter) +# + +from __future__ import print_function + +import getopt +import string +import sys + +from PIL import Image + + +def usage(): + print("PIL Convert 0.5/1998-12-30 -- convert image files") + print("Usage: pilconvert [option] infile outfile") + print() + print("Options:") + print() + print(" -c convert to format (default is given by extension)") + print() + print(" -g convert to greyscale") + print(" -p convert to palette image (using standard palette)") + print(" -r convert to rgb") + print() + print(" -o optimize output (trade speed for size)") + print(" -q set compression quality (0-100, JPEG only)") + print() + print(" -f list supported file formats") + sys.exit(1) + +if len(sys.argv) == 1: + usage() + +try: + opt, argv = getopt.getopt(sys.argv[1:], "c:dfgopq:r") +except getopt.error as v: + print(v) + sys.exit(1) + +output_format = None +convert = None + +options = {} + +for o, a in opt: + + if o == "-f": + Image.init() + id = sorted(Image.ID) + print("Supported formats (* indicates output format):") + for i in id: + if i in Image.SAVE: + print(i+"*", end=' ') + else: + print(i, end=' ') + sys.exit(1) + + elif o == "-c": + output_format = a + + if o == "-g": + convert = "L" + elif o == "-p": + convert = "P" + elif o == "-r": + convert = "RGB" + + elif o == "-o": + options["optimize"] = 1 + elif o == "-q": + options["quality"] = string.atoi(a) + +if len(argv) != 2: + usage() + +try: + im = Image.open(argv[0]) + if convert and im.mode != convert: + im.draft(convert, im.size) + im = im.convert(convert) + if output_format: + im.save(argv[1], output_format, **options) + else: + im.save(argv[1], **options) +except: + print("cannot convert image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) diff --git a/bin/pildriver.py b/bin/pildriver.py new file mode 100755 index 000000000..0d0607364 --- /dev/null +++ b/bin/pildriver.py @@ -0,0 +1,526 @@ +#!/Users/admin/SHARE/bin/python3.5 +"""PILdriver, an image-processing calculator using PIL. + +An instance of class PILDriver is essentially a software stack machine +(Polish-notation interpreter) for sequencing PIL image +transformations. The state of the instance is the interpreter stack. + +The only method one will normally invoke after initialization is the +`execute' method. This takes an argument list of tokens, pushes them +onto the instance's stack, and then tries to clear the stack by +successive evaluation of PILdriver operators. Any part of the stack +not cleaned off persists and is part of the evaluation context for +the next call of the execute method. + +PILDriver doesn't catch any exceptions, on the theory that these +are actually diagnostic information that should be interpreted by +the calling code. + +When called as a script, the command-line arguments are passed to +a PILDriver instance. If there are no command-line arguments, the +module runs an interactive interpreter, each line of which is split into +space-separated tokens and passed to the execute method. + +In the method descriptions below, a first line beginning with the string +`usage:' means this method can be invoked with the token that follows +it. Following <>-enclosed arguments describe how the method interprets +the entries on the stack. Each argument specification begins with a +type specification: either `int', `float', `string', or `image'. + +All operations consume their arguments off the stack (use `dup' to +keep copies around). Use `verbose 1' to see the stack state displayed +before each operation. + +Usage examples: + + `show crop 0 0 200 300 open test.png' loads test.png, crops out a portion +of its upper-left-hand corner and displays the cropped portion. + + `save rotated.png rotate 30 open test.tiff' loads test.tiff, rotates it +30 degrees, and saves the result as rotated.png (in PNG format). +""" +# by Eric S. Raymond +# $Id$ + +# TO DO: +# 1. Add PILFont capabilities, once that's documented. +# 2. Add PILDraw operations. +# 3. Add support for composing and decomposing multiple-image files. +# + +from __future__ import print_function + +from PIL import Image + + +class PILDriver(object): + + verbose = 0 + + def do_verbose(self): + """usage: verbose + + Set verbosity flag from top of stack. + """ + self.verbose = int(self.do_pop()) + + # The evaluation stack (internal only) + + stack = [] # Stack of pending operations + + def push(self, item): + "Push an argument onto the evaluation stack." + self.stack.insert(0, item) + + def top(self): + "Return the top-of-stack element." + return self.stack[0] + + # Stack manipulation (callable) + + def do_clear(self): + """usage: clear + + Clear the stack. + """ + self.stack = [] + + def do_pop(self): + """usage: pop + + Discard the top element on the stack. + """ + return self.stack.pop(0) + + def do_dup(self): + """usage: dup + + Duplicate the top-of-stack item. + """ + if hasattr(self, 'format'): # If it's an image, do a real copy + dup = self.stack[0].copy() + else: + dup = self.stack[0] + self.push(dup) + + def do_swap(self): + """usage: swap + + Swap the top-of-stack item with the next one down. + """ + self.stack = [self.stack[1], self.stack[0]] + self.stack[2:] + + # Image module functions (callable) + + def do_new(self): + """usage: new : + + Create and push a greyscale image of given size and color. + """ + xsize = int(self.do_pop()) + ysize = int(self.do_pop()) + color = int(self.do_pop()) + self.push(Image.new("L", (xsize, ysize), color)) + + def do_open(self): + """usage: open + + Open the indicated image, read it, push the image on the stack. + """ + self.push(Image.open(self.do_pop())) + + def do_blend(self): + """usage: blend + + Replace two images and an alpha with the blended image. + """ + image1 = self.do_pop() + image2 = self.do_pop() + alpha = float(self.do_pop()) + self.push(Image.blend(image1, image2, alpha)) + + def do_composite(self): + """usage: composite + + Replace two images and a mask with their composite. + """ + image1 = self.do_pop() + image2 = self.do_pop() + mask = self.do_pop() + self.push(Image.composite(image1, image2, mask)) + + def do_merge(self): + """usage: merge + [ [ []]] + + Merge top-of stack images in a way described by the mode. + """ + mode = self.do_pop() + bandlist = [] + for band in mode: + bandlist.append(self.do_pop()) + self.push(Image.merge(mode, bandlist)) + + # Image class methods + + def do_convert(self): + """usage: convert + + Convert the top image to the given mode. + """ + mode = self.do_pop() + image = self.do_pop() + self.push(image.convert(mode)) + + def do_copy(self): + """usage: copy + + Make and push a true copy of the top image. + """ + self.dup() + + def do_crop(self): + """usage: crop + + + Crop and push a rectangular region from the current image. + """ + left = int(self.do_pop()) + upper = int(self.do_pop()) + right = int(self.do_pop()) + lower = int(self.do_pop()) + image = self.do_pop() + self.push(image.crop((left, upper, right, lower))) + + def do_draft(self): + """usage: draft + + Configure the loader for a given mode and size. + """ + mode = self.do_pop() + xsize = int(self.do_pop()) + ysize = int(self.do_pop()) + self.push(self.draft(mode, (xsize, ysize))) + + def do_filter(self): + """usage: filter + + Process the top image with the given filter. + """ + from PIL import ImageFilter + imageFilter = getattr(ImageFilter, self.do_pop().upper()) + image = self.do_pop() + self.push(image.filter(imageFilter)) + + def do_getbbox(self): + """usage: getbbox + + Push left, upper, right, and lower pixel coordinates of the top image. + """ + bounding_box = self.do_pop().getbbox() + self.push(bounding_box[3]) + self.push(bounding_box[2]) + self.push(bounding_box[1]) + self.push(bounding_box[0]) + + def do_getextrema(self): + """usage: extrema + + Push minimum and maximum pixel values of the top image. + """ + extrema = self.do_pop().extrema() + self.push(extrema[1]) + self.push(extrema[0]) + + def do_offset(self): + """usage: offset + + Offset the pixels in the top image. + """ + xoff = int(self.do_pop()) + yoff = int(self.do_pop()) + image = self.do_pop() + self.push(image.offset(xoff, yoff)) + + def do_paste(self): + """usage: paste + + + Paste figure image into ground with upper left at given offsets. + """ + figure = self.do_pop() + xoff = int(self.do_pop()) + yoff = int(self.do_pop()) + ground = self.do_pop() + if figure.mode == "RGBA": + ground.paste(figure, (xoff, yoff), figure) + else: + ground.paste(figure, (xoff, yoff)) + self.push(ground) + + def do_resize(self): + """usage: resize + + Resize the top image. + """ + ysize = int(self.do_pop()) + xsize = int(self.do_pop()) + image = self.do_pop() + self.push(image.resize((xsize, ysize))) + + def do_rotate(self): + """usage: rotate + + Rotate image through a given angle + """ + angle = int(self.do_pop()) + image = self.do_pop() + self.push(image.rotate(angle)) + + def do_save(self): + """usage: save + + Save image with default options. + """ + filename = self.do_pop() + image = self.do_pop() + image.save(filename) + + def do_save2(self): + """usage: save2 + + Save image with specified options. + """ + filename = self.do_pop() + options = self.do_pop() + image = self.do_pop() + image.save(filename, None, options) + + def do_show(self): + """usage: show + + Display and pop the top image. + """ + self.do_pop().show() + + def do_thumbnail(self): + """usage: thumbnail + + Modify the top image in the stack to contain a thumbnail of itself. + """ + ysize = int(self.do_pop()) + xsize = int(self.do_pop()) + self.top().thumbnail((xsize, ysize)) + + def do_transpose(self): + """usage: transpose + + Transpose the top image. + """ + transpose = self.do_pop().upper() + image = self.do_pop() + self.push(image.transpose(transpose)) + + # Image attributes + + def do_format(self): + """usage: format + + Push the format of the top image onto the stack. + """ + self.push(self.do_pop().format) + + def do_mode(self): + """usage: mode + + Push the mode of the top image onto the stack. + """ + self.push(self.do_pop().mode) + + def do_size(self): + """usage: size + + Push the image size on the stack as (y, x). + """ + size = self.do_pop().size + self.push(size[0]) + self.push(size[1]) + + # ImageChops operations + + def do_invert(self): + """usage: invert + + Invert the top image. + """ + from PIL import ImageChops + self.push(ImageChops.invert(self.do_pop())) + + def do_lighter(self): + """usage: lighter + + Pop the two top images, push an image of the lighter pixels of both. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.lighter(image1, image2)) + + def do_darker(self): + """usage: darker + + Pop the two top images, push an image of the darker pixels of both. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.darker(image1, image2)) + + def do_difference(self): + """usage: difference + + Pop the two top images, push the difference image + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.difference(image1, image2)) + + def do_multiply(self): + """usage: multiply + + Pop the two top images, push the multiplication image. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.multiply(image1, image2)) + + def do_screen(self): + """usage: screen + + Pop the two top images, superimpose their inverted versions. + """ + from PIL import ImageChops + image2 = self.do_pop() + image1 = self.do_pop() + self.push(ImageChops.screen(image1, image2)) + + def do_add(self): + """usage: add + + Pop the two top images, produce the scaled sum with offset. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + scale = float(self.do_pop()) + offset = int(self.do_pop()) + self.push(ImageChops.add(image1, image2, scale, offset)) + + def do_subtract(self): + """usage: subtract + + Pop the two top images, produce the scaled difference with offset. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + scale = float(self.do_pop()) + offset = int(self.do_pop()) + self.push(ImageChops.subtract(image1, image2, scale, offset)) + + # ImageEnhance classes + + def do_color(self): + """usage: color + + Enhance color in the top image. + """ + from PIL import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Color(image) + self.push(enhancer.enhance(factor)) + + def do_contrast(self): + """usage: contrast + + Enhance contrast in the top image. + """ + from PIL import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Contrast(image) + self.push(enhancer.enhance(factor)) + + def do_brightness(self): + """usage: brightness + + Enhance brightness in the top image. + """ + from PIL import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Brightness(image) + self.push(enhancer.enhance(factor)) + + def do_sharpness(self): + """usage: sharpness + + Enhance sharpness in the top image. + """ + from PIL import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Sharpness(image) + self.push(enhancer.enhance(factor)) + + # The interpreter loop + + def execute(self, list): + "Interpret a list of PILDriver commands." + list.reverse() + while len(list) > 0: + self.push(list[0]) + list = list[1:] + if self.verbose: + print("Stack: " + repr(self.stack)) + top = self.top() + if not isinstance(top, str): + continue + funcname = "do_" + top + if not hasattr(self, funcname): + continue + else: + self.do_pop() + func = getattr(self, funcname) + func() + +if __name__ == '__main__': + import sys + + # If we see command-line arguments, interpret them as a stack state + # and execute. Otherwise go interactive. + + driver = PILDriver() + if len(sys.argv[1:]) > 0: + driver.execute(sys.argv[1:]) + else: + print("PILDriver says hello.") + while True: + try: + if sys.version_info[0] >= 3: + line = input('pildriver> ') + else: + line = raw_input('pildriver> ') + except EOFError: + print("\nPILDriver says goodbye.") + break + driver.execute(line.split()) + print(driver.stack) + +# The following sets edit modes for GNU EMACS +# Local Variables: +# mode:python +# End: diff --git a/bin/pilfile.py b/bin/pilfile.py new file mode 100755 index 000000000..ba43609f1 --- /dev/null +++ b/bin/pilfile.py @@ -0,0 +1,101 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library. +# $Id$ +# +# a utility to identify image files +# +# this script identifies image files, extracting size and +# pixel mode information for known file formats. Note that +# you don't need the PIL C extension to use this module. +# +# History: +# 0.0 1995-09-01 fl Created +# 0.1 1996-05-18 fl Modified options, added debugging mode +# 0.2 1996-12-29 fl Added verify mode +# 0.3 1999-06-05 fl Don't mess up on class exceptions (1.5.2 and later) +# 0.4 2003-09-30 fl Expand wildcards on Windows; robustness tweaks +# + +from __future__ import print_function + +import getopt +import glob +import logging +import sys + +from PIL import Image + +if len(sys.argv) == 1: + print("PIL File 0.4/2003-09-30 -- identify image files") + print("Usage: pilfile [option] files...") + print("Options:") + print(" -f list supported file formats") + print(" -i show associated info and tile data") + print(" -v verify file headers") + print(" -q quiet, don't warn for unidentified/missing/broken files") + sys.exit(1) + +try: + opt, args = getopt.getopt(sys.argv[1:], "fqivD") +except getopt.error as v: + print(v) + sys.exit(1) + +verbose = quiet = verify = 0 +logging_level = "WARNING" + +for o, a in opt: + if o == "-f": + Image.init() + id = sorted(Image.ID) + print("Supported formats:") + for i in id: + print(i, end=' ') + sys.exit(1) + elif o == "-i": + verbose = 1 + elif o == "-q": + quiet = 1 + elif o == "-v": + verify = 1 + elif o == "-D": + logging_level = "DEBUG" + +logging.basicConfig(level=logging_level) + + +def globfix(files): + # expand wildcards where necessary + if sys.platform == "win32": + out = [] + for file in files: + if glob.has_magic(file): + out.extend(glob.glob(file)) + else: + out.append(file) + return out + return files + +for file in globfix(args): + try: + im = Image.open(file) + print("%s:" % file, im.format, "%dx%d" % im.size, im.mode, end=' ') + if verbose: + print(im.info, im.tile, end=' ') + print() + if verify: + try: + im.verify() + except: + if not quiet: + print("failed to verify image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) + except IOError as v: + if not quiet: + print(file, "failed:", v) + except: + import traceback + if not quiet: + print(file, "failed:", "unexpected error") + traceback.print_exc(file=sys.stdout) diff --git a/bin/pilfont.py b/bin/pilfont.py new file mode 100755 index 000000000..d112e2524 --- /dev/null +++ b/bin/pilfont.py @@ -0,0 +1,57 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library +# $Id$ +# +# PIL raster font compiler +# +# history: +# 1997-08-25 fl created +# 2002-03-10 fl use "from PIL import" +# + +from __future__ import print_function + +import glob +import sys + +# drivers +from PIL import BdfFontFile +from PIL import PcfFontFile + +VERSION = "0.4" + +if len(sys.argv) <= 1: + print("PILFONT", VERSION, "-- PIL font compiler.") + print() + print("Usage: pilfont fontfiles...") + print() + print("Convert given font files to the PIL raster font format.") + print("This version of pilfont supports X BDF and PCF fonts.") + sys.exit(1) + +files = [] +for f in sys.argv[1:]: + files = files + glob.glob(f) + +for f in files: + + print(f + "...", end=' ') + + try: + + fp = open(f, "rb") + + try: + p = PcfFontFile.PcfFontFile(fp) + except SyntaxError: + fp.seek(0) + p = BdfFontFile.BdfFontFile(fp) + + p.save(f) + + except (SyntaxError, IOError): + print("failed") + + else: + print("OK") diff --git a/bin/pilprint.py b/bin/pilprint.py new file mode 100755 index 000000000..0a1b741d4 --- /dev/null +++ b/bin/pilprint.py @@ -0,0 +1,102 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library. +# $Id$ +# +# print image files to postscript printer +# +# History: +# 0.1 1996-04-20 fl Created +# 0.2 1996-10-04 fl Use draft mode when converting. +# 0.3 2003-05-06 fl Fixed a typo or two. +# + +from __future__ import print_function +import getopt +import os +import sys +import subprocess + +VERSION = "pilprint 0.3/2003-05-05" + +from PIL import Image +from PIL import PSDraw + +letter = (1.0*72, 1.0*72, 7.5*72, 10.0*72) + + +def description(filepath, image): + title = os.path.splitext(os.path.split(filepath)[1])[0] + format = " (%dx%d " + if image.format: + format = " (" + image.format + " %dx%d " + return title + format % image.size + image.mode + ")" + +if len(sys.argv) == 1: + print("PIL Print 0.3/2003-05-05 -- print image files") + print("Usage: pilprint files...") + print("Options:") + print(" -c colour printer (default is monochrome)") + print(" -d debug (show available drivers)") + print(" -p print via lpr (default is stdout)") + print(" -P same as -p but use given printer") + sys.exit(1) + +try: + opt, argv = getopt.getopt(sys.argv[1:], "cdpP:") +except getopt.error as v: + print(v) + sys.exit(1) + +printerArgs = [] # print to stdout +monochrome = 1 # reduce file size for most common case + +for o, a in opt: + if o == "-d": + # debug: show available drivers + Image.init() + print(Image.ID) + sys.exit(1) + elif o == "-c": + # colour printer + monochrome = 0 + elif o == "-p": + # default printer channel + printerArgs = ["lpr"] + elif o == "-P": + # printer channel + printerArgs = ["lpr", "-P%s" % a] + +for filepath in argv: + try: + + im = Image.open(filepath) + + title = description(filepath, im) + + if monochrome and im.mode not in ["1", "L"]: + im.draft("L", im.size) + im = im.convert("L") + + if printerArgs: + p = subprocess.Popen(printerArgs, stdin=subprocess.PIPE) + fp = p.stdin + else: + fp = sys.stdout + + ps = PSDraw.PSDraw(fp) + + ps.begin_document() + ps.setfont("Helvetica-Narrow-Bold", 18) + ps.text((letter[0], letter[3]+24), title) + ps.setfont("Helvetica-Narrow-Bold", 8) + ps.text((letter[0], letter[1]-30), VERSION) + ps.image(letter, im) + ps.end_document() + + if printerArgs: + fp.close() + + except: + print("cannot print image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) diff --git a/bin/pip b/bin/pip new file mode 100755 index 000000000..3d215bf98 --- /dev/null +++ b/bin/pip @@ -0,0 +1,11 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from pip import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bin/pip3 b/bin/pip3 new file mode 100755 index 000000000..3d215bf98 --- /dev/null +++ b/bin/pip3 @@ -0,0 +1,11 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from pip import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bin/pip3.5 b/bin/pip3.5 new file mode 100755 index 000000000..3d215bf98 --- /dev/null +++ b/bin/pip3.5 @@ -0,0 +1,11 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from pip import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bin/player.py b/bin/player.py new file mode 100755 index 000000000..ba4720d48 --- /dev/null +++ b/bin/player.py @@ -0,0 +1,102 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library +# $Id$ +# + +from __future__ import print_function + +try: + from tkinter import * +except ImportError: + from Tkinter import * + +from PIL import Image, ImageTk +import sys + + +# -------------------------------------------------------------------- +# an image animation player + +class UI(Label): + + def __init__(self, master, im): + if isinstance(im, list): + # list of images + self.im = im[1:] + im = self.im[0] + else: + # sequence + self.im = im + + if im.mode == "1": + self.image = ImageTk.BitmapImage(im, foreground="white") + else: + self.image = ImageTk.PhotoImage(im) + + Label.__init__(self, master, image=self.image, bg="black", bd=0) + + self.update() + + try: + duration = im.info["duration"] + except KeyError: + duration = 100 + self.after(duration, self.next) + + def next(self): + + if isinstance(self.im, list): + + try: + im = self.im[0] + del self.im[0] + self.image.paste(im) + except IndexError: + return # end of list + + else: + + try: + im = self.im + im.seek(im.tell() + 1) + self.image.paste(im) + except EOFError: + return # end of file + + try: + duration = im.info["duration"] + except KeyError: + duration = 100 + self.after(duration, self.next) + + self.update_idletasks() + + +# -------------------------------------------------------------------- +# script interface + +if __name__ == "__main__": + + if not sys.argv[1:]: + print("Syntax: python player.py imagefile(s)") + sys.exit(1) + + filename = sys.argv[1] + + root = Tk() + root.title(filename) + + if len(sys.argv) > 2: + # list of images + print("loading...") + im = [] + for filename in sys.argv[1:]: + im.append(Image.open(filename)) + else: + # sequence + im = Image.open(filename) + + UI(root, im).pack() + + root.mainloop() diff --git a/bin/python b/bin/python new file mode 120000 index 000000000..f549cead8 --- /dev/null +++ b/bin/python @@ -0,0 +1 @@ +python3.5 \ No newline at end of file diff --git a/bin/python3 b/bin/python3 new file mode 120000 index 000000000..f549cead8 --- /dev/null +++ b/bin/python3 @@ -0,0 +1 @@ +python3.5 \ No newline at end of file diff --git a/bin/python3.5 b/bin/python3.5 new file mode 120000 index 000000000..7782f2c00 --- /dev/null +++ b/bin/python3.5 @@ -0,0 +1 @@ +/usr/local/bin/python3.5 \ No newline at end of file diff --git a/bin/raven b/bin/raven new file mode 100755 index 000000000..4a5fc51ec --- /dev/null +++ b/bin/raven @@ -0,0 +1,11 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from raven.scripts.runner import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bin/rst2html.py b/bin/rst2html.py new file mode 100755 index 000000000..dec073c99 --- /dev/null +++ b/bin/rst2html.py @@ -0,0 +1,23 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# $Id: rst2html.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing HTML. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates (X)HTML documents from standalone reStructuredText ' + 'sources. ' + default_description) + +publish_cmdline(writer_name='html', description=description) diff --git a/bin/rst2html5.py b/bin/rst2html5.py new file mode 100755 index 000000000..07a9615a1 --- /dev/null +++ b/bin/rst2html5.py @@ -0,0 +1,35 @@ +#!/Users/admin/SHARE/bin/python3.5 +# -*- coding: utf8 -*- +# :Copyright: © 2015 Günter Milde. +# :License: Released under the terms of the `2-Clause BSD license`_, in short: +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. +# This file is offered as-is, without any warranty. +# +# .. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause +# +# Revision: $Revision: 7847 $ +# Date: $Date: 2015-03-17 18:30:47 +0100 (Di, 17. Mär 2015) $ + +""" +A minimal front end to the Docutils Publisher, producing HTML 5 documents. + +The output also conforms to XHTML 1.0 transitional +(except for the doctype declaration). +""" + +try: + import locale # module missing in Jython + locale.setlocale(locale.LC_ALL, '') +except locale.Error: + pass + +from docutils.core import publish_cmdline, default_description + +description = (u'Generates HTML 5 documents from standalone ' + u'reStructuredText sources ' + + default_description) + +publish_cmdline(writer_name='html5', description=description) diff --git a/bin/rst2latex.py b/bin/rst2latex.py new file mode 100755 index 000000000..2717c1f97 --- /dev/null +++ b/bin/rst2latex.py @@ -0,0 +1,26 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# $Id: rst2latex.py 5905 2009-04-16 12:04:49Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing LaTeX. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline + +description = ('Generates LaTeX documents from standalone reStructuredText ' + 'sources. ' + 'Reads from (default is stdin) and writes to ' + ' (default is stdout). See ' + ' for ' + 'the full reference.') + +publish_cmdline(writer_name='latex', description=description) diff --git a/bin/rst2man.py b/bin/rst2man.py new file mode 100755 index 000000000..84eebc9eb --- /dev/null +++ b/bin/rst2man.py @@ -0,0 +1,26 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# Author: +# Contact: grubert@users.sf.net +# Copyright: This module has been placed in the public domain. + +""" +man.py +====== + +This module provides a simple command line interface that uses the +man page writer to output from ReStructuredText source. +""" + +import locale +try: + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description +from docutils.writers import manpage + +description = ("Generates plain unix manual documents. " + default_description) + +publish_cmdline(writer=manpage.Writer(), description=description) diff --git a/bin/rst2odt.py b/bin/rst2odt.py new file mode 100755 index 000000000..b15e6dc2f --- /dev/null +++ b/bin/rst2odt.py @@ -0,0 +1,30 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# $Id: rst2odt.py 5839 2009-01-07 19:09:28Z dkuhlman $ +# Author: Dave Kuhlman +# Copyright: This module has been placed in the public domain. + +""" +A front end to the Docutils Publisher, producing OpenOffice documents. +""" + +import sys +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline_to_binary, default_description +from docutils.writers.odf_odt import Writer, Reader + + +description = ('Generates OpenDocument/OpenOffice/ODF documents from ' + 'standalone reStructuredText sources. ' + default_description) + + +writer = Writer() +reader = Reader() +output = publish_cmdline_to_binary(reader=reader, writer=writer, + description=description) + diff --git a/bin/rst2odt_prepstyles.py b/bin/rst2odt_prepstyles.py new file mode 100755 index 000000000..36525a758 --- /dev/null +++ b/bin/rst2odt_prepstyles.py @@ -0,0 +1,67 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# $Id: rst2odt_prepstyles.py 5839 2009-01-07 19:09:28Z dkuhlman $ +# Author: Dave Kuhlman +# Copyright: This module has been placed in the public domain. + +""" +Fix a word-processor-generated styles.odt for odtwriter use: Drop page size +specifications from styles.xml in STYLE_FILE.odt. +""" + +# +# Author: Michael Schutte + +from lxml import etree +import sys +import zipfile +from tempfile import mkstemp +import shutil +import os + +NAMESPACES = { + "style": "urn:oasis:names:tc:opendocument:xmlns:style:1.0", + "fo": "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" +} + +def prepstyle(filename): + + zin = zipfile.ZipFile(filename) + styles = zin.read("styles.xml") + + root = etree.fromstring(styles) + for el in root.xpath("//style:page-layout-properties", + namespaces=NAMESPACES): + for attr in el.attrib: + if attr.startswith("{%s}" % NAMESPACES["fo"]): + del el.attrib[attr] + + tempname = mkstemp() + zout = zipfile.ZipFile(os.fdopen(tempname[0], "w"), "w", + zipfile.ZIP_DEFLATED) + + for item in zin.infolist(): + if item.filename == "styles.xml": + zout.writestr(item, etree.tostring(root)) + else: + zout.writestr(item, zin.read(item.filename)) + + zout.close() + zin.close() + shutil.move(tempname[1], filename) + + +def main(): + args = sys.argv[1:] + if len(args) != 1: + print >> sys.stderr, __doc__ + print >> sys.stderr, "Usage: %s STYLE_FILE.odt\n" % sys.argv[0] + sys.exit(1) + filename = args[0] + prepstyle(filename) + +if __name__ == '__main__': + main() + + +# vim:tw=78:sw=4:sts=4:et: diff --git a/bin/rst2pseudoxml.py b/bin/rst2pseudoxml.py new file mode 100755 index 000000000..b190d1e5b --- /dev/null +++ b/bin/rst2pseudoxml.py @@ -0,0 +1,23 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# $Id: rst2pseudoxml.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing pseudo-XML. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates pseudo-XML from standalone reStructuredText ' + 'sources (for testing purposes). ' + default_description) + +publish_cmdline(description=description) diff --git a/bin/rst2s5.py b/bin/rst2s5.py new file mode 100755 index 000000000..3f3046729 --- /dev/null +++ b/bin/rst2s5.py @@ -0,0 +1,24 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# $Id: rst2s5.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Chris Liechti +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing HTML slides using +the S5 template system. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates S5 (X)HTML slideshow documents from standalone ' + 'reStructuredText sources. ' + default_description) + +publish_cmdline(writer_name='s5', description=description) diff --git a/bin/rst2xetex.py b/bin/rst2xetex.py new file mode 100755 index 000000000..6b65a760f --- /dev/null +++ b/bin/rst2xetex.py @@ -0,0 +1,27 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# $Id: rst2xetex.py 7847 2015-03-17 17:30:47Z milde $ +# Author: Guenter Milde +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing Lua/XeLaTeX code. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline + +description = ('Generates LaTeX documents from standalone reStructuredText ' + 'sources for compilation with the Unicode-aware TeX variants ' + 'XeLaTeX or LuaLaTeX. ' + 'Reads from (default is stdin) and writes to ' + ' (default is stdout). See ' + ' for ' + 'the full reference.') + +publish_cmdline(writer_name='xetex', description=description) diff --git a/bin/rst2xml.py b/bin/rst2xml.py new file mode 100755 index 000000000..617ab2746 --- /dev/null +++ b/bin/rst2xml.py @@ -0,0 +1,23 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# $Id: rst2xml.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing Docutils XML. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates Docutils-native XML from standalone ' + 'reStructuredText sources. ' + default_description) + +publish_cmdline(writer_name='xml', description=description) diff --git a/bin/rstpep2html.py b/bin/rstpep2html.py new file mode 100755 index 000000000..e1e7523e1 --- /dev/null +++ b/bin/rstpep2html.py @@ -0,0 +1,25 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# $Id: rstpep2html.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing HTML from PEP +(Python Enhancement Proposal) documents. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates (X)HTML from reStructuredText-format PEP files. ' + + default_description) + +publish_cmdline(reader_name='pep', writer_name='pep_html', + description=description) diff --git a/bin/sharectl b/bin/sharectl new file mode 100755 index 000000000..fbe549688 --- /dev/null +++ b/bin/sharectl @@ -0,0 +1,12 @@ +#!/Users/admin/SHARE/bin/python +# EASY-INSTALL-ENTRY-SCRIPT: 'share','console_scripts','sharectl' +__requires__ = 'share' +import re +import sys +from pkg_resources import load_entry_point + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit( + load_entry_point('share', 'console_scripts', 'sharectl')() + ) diff --git a/bin/thresholder.py b/bin/thresholder.py new file mode 100755 index 000000000..bc6d8865e --- /dev/null +++ b/bin/thresholder.py @@ -0,0 +1,78 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library +# $Id$ +# +# this demo script illustrates how a 1-bit BitmapImage can be used +# as a dynamically updated overlay +# + +try: + from tkinter import * +except ImportError: + from Tkinter import * + +from PIL import Image, ImageTk +import sys + +# +# an image viewer + + +class UI(Frame): + def __init__(self, master, im, value=128): + Frame.__init__(self, master) + + self.image = im + self.value = value + + self.canvas = Canvas(self, width=im.size[0], height=im.size[1]) + self.backdrop = ImageTk.PhotoImage(im) + self.canvas.create_image(0, 0, image=self.backdrop, anchor=NW) + self.canvas.pack() + + scale = Scale(self, orient=HORIZONTAL, from_=0, to=255, + resolution=1, command=self.update_scale, length=256) + scale.set(value) + scale.bind("", self.redraw) + scale.pack() + + # uncomment the following line for instant feedback (might + # be too slow on some platforms) + # self.redraw() + + def update_scale(self, value): + self.value = float(value) + + self.redraw() + + def redraw(self, event=None): + + # create overlay (note the explicit conversion to mode "1") + im = self.image.point(lambda v, t=self.value: v >= t, "1") + self.overlay = ImageTk.BitmapImage(im, foreground="green") + + # update canvas + self.canvas.delete("overlay") + self.canvas.create_image(0, 0, image=self.overlay, anchor=NW, + tags="overlay") + +# -------------------------------------------------------------------- +# main + +if len(sys.argv) != 2: + print("Usage: thresholder file") + sys.exit(1) + +root = Tk() + +im = Image.open(sys.argv[1]) + +if im.mode != "L": + im = im.convert("L") + +# im.thumbnail((320,200)) + +UI(root, im).pack() + +root.mainloop() diff --git a/bin/viewer.py b/bin/viewer.py new file mode 100755 index 000000000..217026e9a --- /dev/null +++ b/bin/viewer.py @@ -0,0 +1,54 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library +# $Id$ +# + +from __future__ import print_function + +try: + from tkinter import Tk, Label +except ImportError: + from Tkinter import Tk, Label + +from PIL import Image, ImageTk + +# +# an image viewer + + +class UI(Label): + + def __init__(self, master, im): + + if im.mode == "1": + # bitmap image + self.image = ImageTk.BitmapImage(im, foreground="white") + Label.__init__(self, master, image=self.image, bg="black", bd=0) + + else: + # photo image + self.image = ImageTk.PhotoImage(im) + Label.__init__(self, master, image=self.image, bd=0) + +# +# script interface + +if __name__ == "__main__": + + import sys + + if not sys.argv[1:]: + print("Syntax: python viewer.py imagefile") + sys.exit(1) + + filename = sys.argv[1] + + root = Tk() + root.title(filename) + + im = Image.open(filename) + + UI(root, im).pack() + + root.mainloop() diff --git a/include/site/python3.5/greenlet/greenlet.h b/include/site/python3.5/greenlet/greenlet.h new file mode 100644 index 000000000..0eceecbf1 --- /dev/null +++ b/include/site/python3.5/greenlet/greenlet.h @@ -0,0 +1,148 @@ +/* vim:set noet ts=8 sw=8 : */ + +/* Greenlet object interface */ + +#ifndef Py_GREENLETOBJECT_H +#define Py_GREENLETOBJECT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define GREENLET_VERSION "0.4.12" + +typedef struct _greenlet { + PyObject_HEAD + char* stack_start; + char* stack_stop; + char* stack_copy; + intptr_t stack_saved; + struct _greenlet* stack_prev; + struct _greenlet* parent; + PyObject* run_info; + struct _frame* top_frame; + int recursion_depth; + PyObject* weakreflist; + PyObject* exc_type; + PyObject* exc_value; + PyObject* exc_traceback; + PyObject* dict; +} PyGreenlet; + +#define PyGreenlet_Check(op) PyObject_TypeCheck(op, &PyGreenlet_Type) +#define PyGreenlet_MAIN(op) (((PyGreenlet*)(op))->stack_stop == (char*) -1) +#define PyGreenlet_STARTED(op) (((PyGreenlet*)(op))->stack_stop != NULL) +#define PyGreenlet_ACTIVE(op) (((PyGreenlet*)(op))->stack_start != NULL) +#define PyGreenlet_GET_PARENT(op) (((PyGreenlet*)(op))->parent) + +#if (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 7) || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 1) || PY_MAJOR_VERSION > 3 +#define GREENLET_USE_PYCAPSULE +#endif + +/* C API functions */ + +/* Total number of symbols that are exported */ +#define PyGreenlet_API_pointers 8 + +#define PyGreenlet_Type_NUM 0 +#define PyExc_GreenletError_NUM 1 +#define PyExc_GreenletExit_NUM 2 + +#define PyGreenlet_New_NUM 3 +#define PyGreenlet_GetCurrent_NUM 4 +#define PyGreenlet_Throw_NUM 5 +#define PyGreenlet_Switch_NUM 6 +#define PyGreenlet_SetParent_NUM 7 + +#ifndef GREENLET_MODULE +/* This section is used by modules that uses the greenlet C API */ +static void **_PyGreenlet_API = NULL; + +#define PyGreenlet_Type (*(PyTypeObject *) _PyGreenlet_API[PyGreenlet_Type_NUM]) + +#define PyExc_GreenletError \ + ((PyObject *) _PyGreenlet_API[PyExc_GreenletError_NUM]) + +#define PyExc_GreenletExit \ + ((PyObject *) _PyGreenlet_API[PyExc_GreenletExit_NUM]) + +/* + * PyGreenlet_New(PyObject *args) + * + * greenlet.greenlet(run, parent=None) + */ +#define PyGreenlet_New \ + (* (PyGreenlet * (*)(PyObject *run, PyGreenlet *parent)) \ + _PyGreenlet_API[PyGreenlet_New_NUM]) + +/* + * PyGreenlet_GetCurrent(void) + * + * greenlet.getcurrent() + */ +#define PyGreenlet_GetCurrent \ + (* (PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM]) + +/* + * PyGreenlet_Throw( + * PyGreenlet *greenlet, + * PyObject *typ, + * PyObject *val, + * PyObject *tb) + * + * g.throw(...) + */ +#define PyGreenlet_Throw \ + (* (PyObject * (*) \ + (PyGreenlet *self, PyObject *typ, PyObject *val, PyObject *tb)) \ + _PyGreenlet_API[PyGreenlet_Throw_NUM]) + +/* + * PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args) + * + * g.switch(*args, **kwargs) + */ +#define PyGreenlet_Switch \ + (* (PyObject * (*)(PyGreenlet *greenlet, PyObject *args, PyObject *kwargs)) \ + _PyGreenlet_API[PyGreenlet_Switch_NUM]) + +/* + * PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent) + * + * g.parent = new_parent + */ +#define PyGreenlet_SetParent \ + (* (int (*)(PyGreenlet *greenlet, PyGreenlet *nparent)) \ + _PyGreenlet_API[PyGreenlet_SetParent_NUM]) + +/* Macro that imports greenlet and initializes C API */ +#ifdef GREENLET_USE_PYCAPSULE +#define PyGreenlet_Import() \ +{ \ + _PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \ +} +#else +#define PyGreenlet_Import() \ +{ \ + PyObject *module = PyImport_ImportModule("greenlet"); \ + if (module != NULL) { \ + PyObject *c_api_object = PyObject_GetAttrString( \ + module, "_C_API"); \ + if (c_api_object != NULL && PyCObject_Check(c_api_object)) { \ + _PyGreenlet_API = \ + (void **) PyCObject_AsVoidPtr(c_api_object); \ + Py_DECREF(c_api_object); \ + } \ + Py_DECREF(module); \ + } \ +} +#endif + +#endif /* GREENLET_MODULE */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_GREENLETOBJECT_H */ diff --git a/pip-selfcheck.json b/pip-selfcheck.json new file mode 100644 index 000000000..415c30e36 --- /dev/null +++ b/pip-selfcheck.json @@ -0,0 +1 @@ +{"last_check":"2017-06-22T15:35:52Z","pypi_version":"9.0.1"} \ No newline at end of file diff --git a/pyvenv.cfg b/pyvenv.cfg new file mode 100644 index 000000000..aba37e81a --- /dev/null +++ b/pyvenv.cfg @@ -0,0 +1,3 @@ +home = /usr/local/bin +include-system-site-packages = false +version = 3.5.3 diff --git a/share/doc/networkx-1.11/INSTALL.txt b/share/doc/networkx-1.11/INSTALL.txt new file mode 100644 index 000000000..0df6ae25f --- /dev/null +++ b/share/doc/networkx-1.11/INSTALL.txt @@ -0,0 +1,3 @@ +See doc/source/install.rst +or +http://networkx.github.io/documentation/latest/install.html diff --git a/share/doc/networkx-1.11/LICENSE.txt b/share/doc/networkx-1.11/LICENSE.txt new file mode 100644 index 000000000..5d07e773c --- /dev/null +++ b/share/doc/networkx-1.11/LICENSE.txt @@ -0,0 +1,40 @@ +License +======= +NetworkX is distributed with the BSD license. + +:: + + Copyright (C) 2004-2016, NetworkX Developers + Aric Hagberg + Dan Schult + Pieter Swart + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of the NetworkX Developers nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/share/doc/networkx-1.11/examples/3d_drawing/mayavi2_spring.py b/share/doc/networkx-1.11/examples/3d_drawing/mayavi2_spring.py new file mode 100644 index 000000000..b9eda22b7 --- /dev/null +++ b/share/doc/networkx-1.11/examples/3d_drawing/mayavi2_spring.py @@ -0,0 +1,37 @@ +# needs mayavi2 +# run with ipython -wthread +import networkx as nx +import numpy as np +from enthought.mayavi import mlab + +# some graphs to try +#H=nx.krackhardt_kite_graph() +#H=nx.Graph();H.add_edge('a','b');H.add_edge('a','c');H.add_edge('a','d') +#H=nx.grid_2d_graph(4,5) +H=nx.cycle_graph(20) + +# reorder nodes from 0,len(G)-1 +G=nx.convert_node_labels_to_integers(H) +# 3d spring layout +pos=nx.spring_layout(G,dim=3) +# numpy array of x,y,z positions in sorted node order +xyz=np.array([pos[v] for v in sorted(G)]) +# scalar colors +scalars=np.array(G.nodes())+5 + +mlab.figure(1, bgcolor=(0, 0, 0)) +mlab.clf() + +pts = mlab.points3d(xyz[:,0], xyz[:,1], xyz[:,2], + scalars, + scale_factor=0.1, + scale_mode='none', + colormap='Blues', + resolution=20) + +pts.mlab_source.dataset.lines = np.array(G.edges()) +tube = mlab.pipeline.tube(pts, tube_radius=0.01) +mlab.pipeline.surface(tube, color=(0.8, 0.8, 0.8)) + +mlab.savefig('mayavi2_spring.png') +# mlab.show() # interactive window diff --git a/share/doc/networkx-1.11/examples/advanced/eigenvalues.py b/share/doc/networkx-1.11/examples/advanced/eigenvalues.py new file mode 100644 index 000000000..dffa429ac --- /dev/null +++ b/share/doc/networkx-1.11/examples/advanced/eigenvalues.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +""" +Create an G{n,m} random graph and compute the eigenvalues. +Requires numpy and matplotlib. +""" +import networkx as nx +import numpy.linalg +import matplotlib.pyplot as plt + +n = 1000 # 1000 nodes +m = 5000 # 5000 edges +G = nx.gnm_random_graph(n,m) + +L = nx.normalized_laplacian_matrix(G) +e = numpy.linalg.eigvals(L.A) +print("Largest eigenvalue:", max(e)) +print("Smallest eigenvalue:", min(e)) +plt.hist(e,bins=100) # histogram with 100 bins +plt.xlim(0,2) # eigenvalues between 0 and 2 +plt.show() diff --git a/share/doc/networkx-1.11/examples/advanced/heavy_metal_umlaut.py b/share/doc/networkx-1.11/examples/advanced/heavy_metal_umlaut.py new file mode 100644 index 000000000..9055771e2 --- /dev/null +++ b/share/doc/networkx-1.11/examples/advanced/heavy_metal_umlaut.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Example using unicode strings as graph labels. + +Also shows creative use of the Heavy Metal Umlaut: +http://en.wikipedia.org/wiki/Heavy_metal_umlaut + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2006-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as NX +try: + import pylab as P +except ImportError: + pass + +try: + hd='H' + unichr(252) + 'sker D' + unichr(252) + mh='Mot' + unichr(246) + 'rhead' + mc='M' + unichr(246) + 'tley Cr' + unichr(252) + 'e' + st='Sp' + unichr(305) + 'n' + unichr(776) + 'al Tap' + q='Queensr' + unichr(255) + 'che' + boc='Blue ' + unichr(214) +'yster Cult' + dt='Deatht' + unichr(246) + 'ngue' +except NameError: + hd='H' + chr(252) + 'sker D' + chr(252) + mh='Mot' + chr(246) + 'rhead' + mc='M' + chr(246) + 'tley Cr' + chr(252) + 'e' + st='Sp' + chr(305) + 'n' + chr(776) + 'al Tap' + q='Queensr' + chr(255) + 'che' + boc='Blue ' + chr(214) +'yster Cult' + dt='Deatht' + chr(246) + 'ngue' + +G=NX.Graph() +G.add_edge(hd,mh) +G.add_edge(mc,st) +G.add_edge(boc,mc) +G.add_edge(boc,dt) +G.add_edge(st,dt) +G.add_edge(q,st) +G.add_edge(dt,mh) +G.add_edge(st,mh) + +# write in UTF-8 encoding +fh=open('edgelist.utf-8','wb') +fh.write('# -*- coding: utf-8 -*-\n'.encode('utf-8')) # encoding hint for emacs +NX.write_multiline_adjlist(G,fh,delimiter='\t', encoding = 'utf-8') + +# read and store in UTF-8 +fh=open('edgelist.utf-8','rb') +H=NX.read_multiline_adjlist(fh,delimiter='\t', encoding = 'utf-8') + +for n in G.nodes(): + if n not in H: + print(False) + +print(G.nodes()) + +try: + pos=NX.spring_layout(G) + NX.draw(G,pos,font_size=16,with_labels=False) + for p in pos: # raise text positions + pos[p][1]+=0.07 + NX.draw_networkx_labels(G,pos) + P.show() +except: + pass + + diff --git a/share/doc/networkx-1.11/examples/advanced/iterated_dynamical_systems.py b/share/doc/networkx-1.11/examples/advanced/iterated_dynamical_systems.py new file mode 100644 index 000000000..2355b24ac --- /dev/null +++ b/share/doc/networkx-1.11/examples/advanced/iterated_dynamical_systems.py @@ -0,0 +1,199 @@ +""" +Digraphs from Integer-valued Iterated Functions +=============================================== + + +Sums of cubes on 3N +------------------- + +The number 153 has a curious property. + +Let 3N={3,6,9,12,...} be the set of positive multiples of 3. Define an +iterative process f:3N->3N as follows: for a given n, take each digit +of n (in base 10), cube it and then sum the cubes to obtain f(n). + +When this process is repeated, the resulting series n, f(n), f(f(n)),... +terminate in 153 after a finite number of iterations (the process ends +because 153 = 1**3 + 5**3 + 3**3). + +In the language of discrete dynamical systems, 153 is the global +attractor for the iterated map f restricted to the set 3N. + +For example: take the number 108 + +f(108) = 1**3 + 0**3 + 8**3 = 513 + +and + +f(513) = 5**3 + 1**3 + 3**3 = 153 + +So, starting at 108 we reach 153 in two iterations, +represented as: + +108->513->153 + +Computing all orbits of 3N up to 10**5 reveals that the attractor +153 is reached in a maximum of 14 iterations. In this code we +show that 13 cycles is the maximum required for all integers (in 3N) +less than 10,000. + +The smallest number that requires 13 iterations to reach 153, is 177, i.e., + +177->687->1071->345->216->225->141->66->432->99->1458->702->351->153 + +The resulting large digraphs are useful for testing network software. + +The general problem +------------------- + +Given numbers n, a power p and base b, define F(n; p, b) as the sum of +the digits of n (in base b) raised to the power p. The above example +corresponds to f(n)=F(n; 3,10), and below F(n; p, b) is implemented as +the function powersum(n,p,b). The iterative dynamical system defined by +the mapping n:->f(n) above (over 3N) converges to a single fixed point; +153. Applying the map to all positive integers N, leads to a discrete +dynamical process with 5 fixed points: 1, 153, 370, 371, 407. Modulo 3 +those numbers are 1, 0, 1, 2, 2. The function f above has the added +property that it maps a multiple of 3 to another multiple of 3; i.e. it +is invariant on the subset 3N. + + +The squaring of digits (in base 10) result in cycles and the +single fixed point 1. I.e., from a certain point on, the process +starts repeating itself. + +keywords: "Recurring Digital Invariant", "Narcissistic Number", +"Happy Number" + +The 3n+1 problem +---------------- + +There is a rich history of mathematical recreations +associated with discrete dynamical systems. The most famous +is the Collatz 3n+1 problem. See the function +collatz_problem_digraph below. The Collatz conjecture +--- that every orbit returrns to the fixed point 1 in finite time +--- is still unproven. Even the great Paul Erdos said "Mathematics +is not yet ready for such problems", and offered $500 +for its solution. + +keywords: "3n+1", "3x+1", "Collatz problem", "Thwaite's conjecture" + + +""" +from networkx import * +from math import * + + +nmax=10000 +p=3 +mach_eps=0.00000000001 + +def digitsrep(n,b=10): + """Return list of digits comprising n represented in base b. + n must be a nonnegative integer""" + + # very inefficient if you only work with base 10 + dlist=[] + if n<=0: + return [0] + maxpow=int(floor( log(n)/log(b) + mach_eps )) + pow=maxpow + while pow>=0: + x=int(floor(n // b**pow)) + dlist.append(x) + n=n-x*b**pow + pow=pow-1 + return dlist + +def powersum(n,p,b=10): + """Return sum of digits of n (in base b) raised to the power p.""" + dlist=digitsrep(n,b) + sum=0 + for k in dlist: + sum+=k**p + return sum + +def attractor153_graph(n,p,multiple=3,b=10): + """Return digraph of iterations of powersum(n,3,10).""" + G=DiGraph() + for k in range(1,n+1): + if k%multiple==0 and k not in G: + k1=k + knext=powersum(k1,p,b) + while k1!=knext: + G.add_edge(k1,knext) + k1=knext + knext=powersum(k1,p,b) + return G + +def squaring_cycle_graph_old(n,b=10): + """Return digraph of iterations of powersum(n,2,10).""" + G=DiGraph() + for k in range(1,n+1): + k1=k + G.add_node(k1) # case k1==knext, at least add node + knext=powersum(k1,2,b) + G.add_edge(k1,knext) + while k1!=knext: # stop if fixed point + k1=knext + knext=powersum(k1,2,b) + G.add_edge(k1,knext) + if G.out_degree(knext) >=1: + # knext has already been iterated in and out + break + return G + +def sum_of_digits_graph(nmax,b=10): + def f(n): return powersum(n,1,b) + return discrete_dynamics_digraph(nmax,f) + +def squaring_cycle_digraph(nmax,b=10): + def f(n): return powersum(n,2,b) + return discrete_dynamics_digraph(nmax,f) + +def cubing_153_digraph(nmax): + def f(n): return powersum(n,3,10) + return discrete_dynamics_digraph(nmax,f) + +def discrete_dynamics_digraph(nmax,f,itermax=50000): + G=DiGraph() + for k in range(1,nmax+1): + kold=k + G.add_node(kold) + knew=f(kold) + G.add_edge(kold,knew) + while kold!=knew and kold<=1: + # knew has already been iterated in and out + break + return G + +def collatz_problem_digraph(nmax): + def f(n): + if n%2==0: + return n // 2 + else: + return 3*n+1 + return discrete_dynamics_digraph(nmax,f) + +def fixed_points(G): + """Return a list of fixed points for the discrete dynamical + system represented by the digraph G. + """ + return [n for n in G if G.out_degree(n)==0] + + +if __name__ == "__main__": + nmax=10000 + print("Building cubing_153_digraph(%d)"% nmax) + G=cubing_153_digraph(nmax) + print("Resulting digraph has", len(G), "nodes and", + G.size()," edges") + print("Shortest path from 177 to 153 is:") + print(shortest_path(G,177,153)) + print("fixed points are %s" % fixed_points(G)) diff --git a/share/doc/networkx-1.11/examples/advanced/parallel_betweenness.py b/share/doc/networkx-1.11/examples/advanced/parallel_betweenness.py new file mode 100644 index 000000000..45cafd582 --- /dev/null +++ b/share/doc/networkx-1.11/examples/advanced/parallel_betweenness.py @@ -0,0 +1,73 @@ +""" +Example of parallel implementation of betweenness centrality using the +multiprocessing module from Python Standard Library. + +The function betweenness centrality accepts a bunch of nodes and computes +the contribution of those nodes to the betweenness centrality of the whole +network. Here we divide the network in chunks of nodes and we compute their +contribution to the betweenness centrality of the whole network. +""" + +from multiprocessing import Pool +import time +import itertools +import networkx as nx + + +def chunks(l, n): + """Divide a list of nodes `l` in `n` chunks""" + l_c = iter(l) + while 1: + x = tuple(itertools.islice(l_c, n)) + if not x: + return + yield x + + +def _betmap(G_normalized_weight_sources_tuple): + """Pool for multiprocess only accepts functions with one argument. + This function uses a tuple as its only argument. We use a named tuple for + python 3 compatibility, and then unpack it when we send it to + `betweenness_centrality_source` + """ + return nx.betweenness_centrality_source(*G_normalized_weight_sources_tuple) + + +def betweenness_centrality_parallel(G, processes=None): + """Parallel betweenness centrality function""" + p = Pool(processes=processes) + node_divisor = len(p._pool)*4 + node_chunks = list(chunks(G.nodes(), int(G.order()/node_divisor))) + num_chunks = len(node_chunks) + bt_sc = p.map(_betmap, + zip([G]*num_chunks, + [True]*num_chunks, + [None]*num_chunks, + node_chunks)) + + # Reduce the partial solutions + bt_c = bt_sc[0] + for bt in bt_sc[1:]: + for n in bt: + bt_c[n] += bt[n] + return bt_c + +if __name__ == "__main__": + G_ba = nx.barabasi_albert_graph(1000, 3) + G_er = nx.gnp_random_graph(1000, 0.01) + G_ws = nx.connected_watts_strogatz_graph(1000, 4, 0.1) + for G in [G_ba, G_er, G_ws]: + print("") + print("Computing betweenness centrality for:") + print(nx.info(G)) + print("\tParallel version") + start = time.time() + bt = betweenness_centrality_parallel(G) + print("\t\tTime: %.4F" % (time.time()-start)) + print("\t\tBetweenness centrality for node 0: %.5f" % (bt[0])) + print("\tNon-Parallel version") + start = time.time() + bt = nx.betweenness_centrality(G) + print("\t\tTime: %.4F seconds" % (time.time()-start)) + print("\t\tBetweenness centrality for node 0: %.5f" % (bt[0])) + print("") diff --git a/share/doc/networkx-1.11/examples/algorithms/blockmodel.py b/share/doc/networkx-1.11/examples/algorithms/blockmodel.py new file mode 100644 index 000000000..2fed34764 --- /dev/null +++ b/share/doc/networkx-1.11/examples/algorithms/blockmodel.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +Example of creating a block model using the blockmodel function in NX. Data used is the Hartford, CT drug users network: + +@article{, + title = {Social Networks of Drug Users in {High-Risk} Sites: Finding the Connections}, + volume = {6}, + shorttitle = {Social Networks of Drug Users in {High-Risk} Sites}, + url = {http://dx.doi.org/10.1023/A:1015457400897}, + doi = {10.1023/A:1015457400897}, + number = {2}, + journal = {{AIDS} and Behavior}, + author = {Margaret R. Weeks and Scott Clair and Stephen P. Borgatti and Kim Radda and Jean J. Schensul}, + month = jun, + year = {2002}, + pages = {193--206} +} + +""" +# Authors: Drew Conway , Aric Hagberg + +from collections import defaultdict +import networkx as nx +import numpy +from scipy.cluster import hierarchy +from scipy.spatial import distance +import matplotlib.pyplot as plt + + +def create_hc(G): + """Creates hierarchical cluster of graph G from distance matrix""" + path_length=nx.all_pairs_shortest_path_length(G) + distances=numpy.zeros((len(G),len(G))) + for u,p in path_length.items(): + for v,d in p.items(): + distances[u][v]=d + # Create hierarchical cluster + Y=distance.squareform(distances) + Z=hierarchy.complete(Y) # Creates HC using farthest point linkage + # This partition selection is arbitrary, for illustrive purposes + membership=list(hierarchy.fcluster(Z,t=1.15)) + # Create collection of lists for blockmodel + partition=defaultdict(list) + for n,p in zip(list(range(len(G))),membership): + partition[p].append(n) + return list(partition.values()) + +if __name__ == '__main__': + G=nx.read_edgelist("hartford_drug.edgelist") + + # Extract largest connected component into graph H + H=nx.connected_component_subgraphs(G)[0] + # Makes life easier to have consecutively labeled integer nodes + H=nx.convert_node_labels_to_integers(H) + # Create parititions with hierarchical clustering + partitions=create_hc(H) + # Build blockmodel graph + BM=nx.blockmodel(H,partitions) + + + # Draw original graph + pos=nx.spring_layout(H,iterations=100) + fig=plt.figure(1,figsize=(6,10)) + ax=fig.add_subplot(211) + nx.draw(H,pos,with_labels=False,node_size=10) + plt.xlim(0,1) + plt.ylim(0,1) + + # Draw block model with weighted edges and nodes sized by number of internal nodes + node_size=[BM.node[x]['nnodes']*10 for x in BM.nodes()] + edge_width=[(2*d['weight']) for (u,v,d) in BM.edges(data=True)] + # Set positions to mean of positions of internal nodes from original graph + posBM={} + for n in BM: + xy=numpy.array([pos[u] for u in BM.node[n]['graph']]) + posBM[n]=xy.mean(axis=0) + ax=fig.add_subplot(212) + nx.draw(BM,posBM,node_size=node_size,width=edge_width,with_labels=False) + plt.xlim(0,1) + plt.ylim(0,1) + plt.axis('off') + plt.savefig('hartford_drug_block_model.png') diff --git a/share/doc/networkx-1.11/examples/algorithms/davis_club.py b/share/doc/networkx-1.11/examples/algorithms/davis_club.py new file mode 100644 index 000000000..68fab4bf2 --- /dev/null +++ b/share/doc/networkx-1.11/examples/algorithms/davis_club.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +""" +Davis Southern Club Women + +Shows how to make unipartite projections of the graph and compute the +properties of those graphs. + +These data were collected by Davis et al. in the 1930s. +They represent observed attendance at 14 social events by 18 Southern women. +The graph is bipartite (clubs, women). +""" +import networkx as nx +import networkx.algorithms.bipartite as bipartite + +G = nx.davis_southern_women_graph() +women = G.graph['top'] +clubs = G.graph['bottom'] + +print("Biadjacency matrix") +print(bipartite.biadjacency_matrix(G,women,clubs)) + +# project bipartite graph onto women nodes +W = bipartite.projected_graph(G, women) +print('') +print("#Friends, Member") +for w in women: + print('%d %s' % (W.degree(w),w)) + +# project bipartite graph onto women nodes keeping number of co-occurence +# the degree computed is weighted and counts the total number of shared contacts +W = bipartite.weighted_projected_graph(G, women) +print('') +print("#Friend meetings, Member") +for w in women: + print('%d %s' % (W.degree(w,weight='weight'),w)) + diff --git a/share/doc/networkx-1.11/examples/algorithms/hartford_drug.edgelist b/share/doc/networkx-1.11/examples/algorithms/hartford_drug.edgelist new file mode 100644 index 000000000..c1e92c8eb --- /dev/null +++ b/share/doc/networkx-1.11/examples/algorithms/hartford_drug.edgelist @@ -0,0 +1,338 @@ +# source target +1 2 +1 10 +2 1 +2 10 +3 7 +4 7 +4 209 +5 132 +6 150 +7 3 +7 4 +7 9 +8 106 +8 115 +9 1 +9 2 +9 7 +10 1 +10 2 +11 133 +11 218 +12 88 +13 214 +14 24 +14 52 +16 10 +16 19 +17 64 +17 78 +18 55 +18 103 +18 163 +19 18 +20 64 +20 180 +21 16 +21 22 +22 21 +22 64 +22 106 +23 20 +23 22 +23 64 +24 14 +24 31 +24 122 +27 115 +28 29 +29 28 +30 19 +31 24 +31 32 +31 122 +31 147 +31 233 +32 31 +32 86 +34 35 +34 37 +35 34 +35 43 +36 132 +36 187 +37 38 +37 90 +37 282 +38 42 +38 43 +38 210 +40 20 +42 15 +42 38 +43 34 +43 35 +43 38 +45 107 +46 61 +46 72 +48 23 +49 30 +49 64 +49 108 +49 115 +49 243 +50 30 +50 47 +50 55 +50 125 +50 163 +52 218 +52 224 +54 111 +54 210 +55 65 +55 67 +55 105 +55 108 +55 222 +56 18 +56 64 +57 65 +57 125 +58 20 +58 30 +58 50 +58 103 +58 180 +59 164 +63 125 +64 8 +64 50 +64 70 +64 256 +66 20 +66 84 +66 106 +66 125 +67 22 +67 50 +67 113 +68 50 +70 50 +70 64 +71 72 +74 29 +74 75 +74 215 +75 74 +75 215 +76 58 +76 104 +77 103 +78 64 +78 68 +80 207 +80 210 +82 8 +82 77 +82 83 +82 97 +82 163 +83 82 +83 226 +83 243 +84 29 +84 154 +87 101 +87 189 +89 90 +90 89 +90 94 +91 86 +92 19 +92 30 +92 106 +94 72 +94 89 +94 90 +95 30 +96 75 +96 256 +97 80 +97 128 +98 86 +100 86 +101 87 +103 77 +103 104 +104 58 +104 77 +104 103 +106 22 +107 38 +107 114 +107 122 +108 49 +108 55 +111 121 +111 128 +111 210 +113 253 +114 107 +116 30 +116 140 +118 129 +118 138 +120 88 +121 128 +122 31 +123 32 +124 244 +125 132 +126 163 +126 180 +128 38 +128 111 +129 118 +132 29 +132 30 +133 30 +134 135 +134 150 +135 134 +137 144 +138 118 +138 129 +139 142 +141 157 +141 163 +142 139 +143 2 +144 137 +145 151 +146 137 +146 165 +146 169 +146 171 +147 31 +147 128 +148 146 +148 169 +148 171 +148 282 +149 128 +149 148 +149 172 +150 86 +151 145 +152 4 +153 134 +154 155 +156 161 +157 141 +161 156 +165 144 +165 148 +167 149 +169 15 +169 148 +169 171 +170 115 +170 173 +170 183 +170 202 +171 72 +171 148 +171 169 +173 170 +175 100 +176 10 +178 181 +181 178 +182 38 +182 171 +183 96 +185 50 +186 127 +187 50 +187 65 +188 30 +188 50 +189 87 +189 89 +190 35 +190 38 +190 122 +190 182 +191 54 +191 118 +191 129 +191 172 +192 149 +192 167 +195 75 +197 50 +197 188 +198 218 +198 221 +198 222 +200 65 +200 220 +201 113 +202 156 +203 232 +204 194 +207 38 +207 122 +207 124 +208 30 +208 50 +210 38 +210 207 +211 37 +213 35 +213 38 +214 13 +214 14 +214 171 +214 213 +215 75 +217 39 +218 68 +218 222 +221 198 +222 198 +222 218 +223 39 +225 3 +226 22 +229 65 +230 68 +231 43 +232 95 +232 203 +233 99 +234 68 +234 230 +237 244 +238 145 +242 3 +242 113 +244 237 +249 96 +250 156 +252 65 +254 65 +258 113 +268 4 +270 183 +272 6 +275 96 +280 183 +280 206 +282 37 +285 75 +290 285 +293 290 \ No newline at end of file diff --git a/share/doc/networkx-1.11/examples/algorithms/krackhardt_centrality.py b/share/doc/networkx-1.11/examples/algorithms/krackhardt_centrality.py new file mode 100644 index 000000000..71dee8f18 --- /dev/null +++ b/share/doc/networkx-1.11/examples/algorithms/krackhardt_centrality.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +""" +Centrality measures of Krackhardt social network. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) +# Date: 2005-05-12 14:33:11 -0600 (Thu, 12 May 2005) +# Revision: 998 + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +from networkx import * + +G=krackhardt_kite_graph() + +print("Betweenness") +b=betweenness_centrality(G) +for v in G.nodes(): + print("%0.2d %5.3f"%(v,b[v])) + +print("Degree centrality") +d=degree_centrality(G) +for v in G.nodes(): + print("%0.2d %5.3f"%(v,d[v])) + +print("Closeness centrality") +c=closeness_centrality(G) +for v in G.nodes(): + print("%0.2d %5.3f"%(v,c[v])) diff --git a/share/doc/networkx-1.11/examples/algorithms/rcm.py b/share/doc/networkx-1.11/examples/algorithms/rcm.py new file mode 100644 index 000000000..43fa56f6c --- /dev/null +++ b/share/doc/networkx-1.11/examples/algorithms/rcm.py @@ -0,0 +1,32 @@ +# Cuthill-McKee ordering of matrices +# The reverse Cuthill-McKee algorithm gives a sparse matrix ordering that +# reduces the matrix bandwidth. +# Requires NumPy +# Copyright (C) 2011-2016 by +# Author: Aric Hagberg +# BSD License +import networkx as nx +from networkx.utils import reverse_cuthill_mckee_ordering +import numpy as np + +# build low-bandwidth numpy matrix +G=nx.grid_2d_graph(3,3) +rcm = list(reverse_cuthill_mckee_ordering(G)) +print("ordering",rcm) + +print("unordered Laplacian matrix") +A = nx.laplacian_matrix(G) +x,y = np.nonzero(A) +#print("lower bandwidth:",(y-x).max()) +#print("upper bandwidth:",(x-y).max()) +print("bandwidth: %d"%((y-x).max()+(x-y).max()+1)) +print(A) + +B = nx.laplacian_matrix(G,nodelist=rcm) +print("low-bandwidth Laplacian matrix") +x,y = np.nonzero(B) +#print("lower bandwidth:",(y-x).max()) +#print("upper bandwidth:",(x-y).max()) +print("bandwidth: %d"%((y-x).max()+(x-y).max()+1)) +print(B) + diff --git a/share/doc/networkx-1.11/examples/basic/properties.py b/share/doc/networkx-1.11/examples/basic/properties.py new file mode 100644 index 000000000..9ac87f86e --- /dev/null +++ b/share/doc/networkx-1.11/examples/basic/properties.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +""" +Compute some network properties for the lollipop graph. +""" +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +from networkx import * + +G = lollipop_graph(4,6) + +pathlengths=[] + +print("source vertex {target:length, }") +for v in G.nodes(): + spl=single_source_shortest_path_length(G,v) + print('%s %s' % (v,spl)) + for p in spl.values(): + pathlengths.append(p) + +print('') +print("average shortest path length %s" % (sum(pathlengths)/len(pathlengths))) + +# histogram of path lengths +dist={} +for p in pathlengths: + if p in dist: + dist[p]+=1 + else: + dist[p]=1 + +print('') +print("length #paths") +verts=dist.keys() +for d in sorted(verts): + print('%s %d' % (d,dist[d])) + +print("radius: %d" % radius(G)) +print("diameter: %d" % diameter(G)) +print("eccentricity: %s" % eccentricity(G)) +print("center: %s" % center(G)) +print("periphery: %s" % periphery(G)) +print("density: %s" % density(G)) + diff --git a/share/doc/networkx-1.11/examples/basic/read_write.py b/share/doc/networkx-1.11/examples/basic/read_write.py new file mode 100644 index 000000000..37db0cd7a --- /dev/null +++ b/share/doc/networkx-1.11/examples/basic/read_write.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +""" +Read and write graphs. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +from networkx import * +import sys +G=grid_2d_graph(5,5) # 5x5 grid +try: # Python 2.6+ + write_adjlist(G,sys.stdout) # write adjacency list to screen +except TypeError: # Python 3.x + write_adjlist(G,sys.stdout.buffer) # write adjacency list to screen +# write edgelist to grid.edgelist +write_edgelist(G,path="grid.edgelist",delimiter=":") +# read edgelist from grid.edgelist +H=read_edgelist(path="grid.edgelist",delimiter=":") + diff --git a/share/doc/networkx-1.11/examples/drawing/atlas.py b/share/doc/networkx-1.11/examples/drawing/atlas.py new file mode 100644 index 000000000..418ac6363 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/atlas.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +""" +Atlas of all graphs of 6 nodes or less. + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx +from networkx.generators.atlas import * +from networkx.algorithms.isomorphism.isomorph import graph_could_be_isomorphic as isomorphic +import random + +def atlas6(): + """ Return the atlas of all connected graphs of 6 nodes or less. + Attempt to check for isomorphisms and remove. + """ + + Atlas = graph_atlas_g()[0:208] # 208 + # remove isolated nodes, only connected graphs are left + U = nx.Graph() # graph for union of all graphs in atlas + for G in Atlas: + zerodegree = [n for n in G if G.degree(n)==0] + for n in zerodegree: + G.remove_node(n) + U = nx.disjoint_union(U, G) + + # list of graphs of all connected components + C = nx.connected_component_subgraphs(U) + + UU = nx.Graph() + # do quick isomorphic-like check, not a true isomorphism checker + nlist = [] # list of nonisomorphic graphs + for G in C: + # check against all nonisomorphic graphs so far + if not iso(G, nlist): + nlist.append(G) + UU = nx.disjoint_union(UU, G) # union the nonisomorphic graphs + return UU + +def iso(G1, glist): + """Quick and dirty nonisomorphism checker used to check isomorphisms.""" + for G2 in glist: + if isomorphic(G1, G2): + return True + return False + + +if __name__ == '__main__': + G=atlas6() + + print("graph has %d nodes with %d edges"\ + %(nx.number_of_nodes(G), nx.number_of_edges(G))) + print(nx.number_connected_components(G), "connected components") + + try: + import pygraphviz + from networkx.drawing.nx_agraph import graphviz_layout + except ImportError: + try: + import pydotplus + from networkx.drawing.nx_pydot import graphviz_layout + except ImportError: + raise ImportError("This example needs Graphviz and either " + "PyGraphviz or PyDotPlus") + + import matplotlib.pyplot as plt + plt.figure(1, figsize=(8, 8)) + # layout graphs with positions using graphviz neato + pos = graphviz_layout(G, prog="neato") + # color nodes the same in each connected subgraph + C = nx.connected_component_subgraphs(G) + for g in C: + c = [random.random()] * nx.number_of_nodes(g) # random color... + nx.draw(g, + pos, + node_size=40, + node_color=c, + vmin=0.0, + vmax=1.0, + with_labels=False + ) + plt.savefig("atlas.png", dpi=75) diff --git a/share/doc/networkx-1.11/examples/drawing/chess_masters.py b/share/doc/networkx-1.11/examples/drawing/chess_masters.py new file mode 100644 index 000000000..76e8ae6f7 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/chess_masters.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python + +""" +An example of the MultiDiGraph clas + +The function chess_pgn_graph reads a collection of chess +matches stored in the specified PGN file +(PGN ="Portable Game Notation") +Here the (compressed) default file --- + chess_masters_WCC.pgn.bz2 --- +contains all 685 World Chess Championship matches +from 1886 - 1985. +(data from http://chessproblem.my-free-games.com/chess/games/Download-PGN.php) + +The chess_pgn_graph() function returns a MultiDiGraph +with multiple edges. Each node is +the last name of a chess master. Each edge is directed +from white to black and contains selected game info. + +The key statement in chess_pgn_graph below is + G.add_edge(white, black, game_info) +where game_info is a dict describing each game. + +""" +# Copyright (C) 2006-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx + +# tag names specifying what game info should be +# stored in the dict on each digraph edge +game_details=["Event", + "Date", + "Result", + "ECO", + "Site"] + +def chess_pgn_graph(pgn_file="chess_masters_WCC.pgn.bz2"): + """Read chess games in pgn format in pgn_file. + + Filenames ending in .gz or .bz2 will be uncompressed. + + Return the MultiDiGraph of players connected by a chess game. + Edges contain game data in a dict. + + """ + import bz2 + G=nx.MultiDiGraph() + game={} + datafile = bz2.BZ2File(pgn_file) + lines = (line.decode().rstrip('\r\n') for line in datafile) + for line in lines: + if line.startswith('['): + tag,value=line[1:-1].split(' ',1) + game[str(tag)]=value.strip('"') + else: + # empty line after tag set indicates + # we finished reading game info + if game: + white=game.pop('White') + black=game.pop('Black') + G.add_edge(white, black, **game) + game={} + return G + + +if __name__ == '__main__': + G=chess_pgn_graph() + + ngames=G.number_of_edges() + nplayers=G.number_of_nodes() + + print("Loaded %d chess games between %d players\n"\ + % (ngames,nplayers)) + + # identify connected components + # of the undirected version + Gcc=list(nx.connected_component_subgraphs(G.to_undirected())) + if len(Gcc)>1: + print("Note the disconnected component consisting of:") + print(Gcc[1].nodes()) + + # find all games with B97 opening (as described in ECO) + openings=set([game_info['ECO'] + for (white,black,game_info) in G.edges(data=True)]) + print("\nFrom a total of %d different openings,"%len(openings)) + print('the following games used the Sicilian opening') + print('with the Najdorff 7...Qb6 "Poisoned Pawn" variation.\n') + + for (white,black,game_info) in G.edges(data=True): + if game_info['ECO']=='B97': + print(white,"vs",black) + for k,v in game_info.items(): + print(" ",k,": ",v) + print("\n") + + + try: + import matplotlib.pyplot as plt + except ImportError: + import sys + print("Matplotlib needed for drawing. Skipping") + sys.exit(0) + + # make new undirected graph H without multi-edges + H=nx.Graph(G) + + # edge width is proportional number of games played + edgewidth=[] + for (u,v,d) in H.edges(data=True): + edgewidth.append(len(G.get_edge_data(u,v))) + + # node size is proportional to number of games won + wins=dict.fromkeys(G.nodes(),0.0) + for (u,v,d) in G.edges(data=True): + r=d['Result'].split('-') + if r[0]=='1': + wins[u]+=1.0 + elif r[0]=='1/2': + wins[u]+=0.5 + wins[v]+=0.5 + else: + wins[v]+=1.0 + try: + pos=nx.nx_agraph.graphviz_layout(H) + except: + pos=nx.spring_layout(H,iterations=20) + + plt.rcParams['text.usetex'] = False + plt.figure(figsize=(8,8)) + nx.draw_networkx_edges(H,pos,alpha=0.3,width=edgewidth, edge_color='m') + nodesize=[wins[v]*50 for v in H] + nx.draw_networkx_nodes(H,pos,node_size=nodesize,node_color='w',alpha=0.4) + nx.draw_networkx_edges(H,pos,alpha=0.4,node_size=0,width=1,edge_color='k') + nx.draw_networkx_labels(H,pos,fontsize=14) + font = {'fontname' : 'Helvetica', + 'color' : 'k', + 'fontweight' : 'bold', + 'fontsize' : 14} + plt.title("World Chess Championship Games: 1886 - 1985", font) + + # change font and write text (using data coordinates) + font = {'fontname' : 'Helvetica', + 'color' : 'r', + 'fontweight' : 'bold', + 'fontsize' : 14} + + plt.text(0.5, 0.97, "edge width = # games played", + horizontalalignment='center', + transform=plt.gca().transAxes) + plt.text(0.5, 0.94, "node size = # games won", + horizontalalignment='center', + transform=plt.gca().transAxes) + + plt.axis('off') + plt.savefig("chess_masters.png",dpi=75) + print("Wrote chess_masters.png") + plt.show() # display diff --git a/share/doc/networkx-1.11/examples/drawing/chess_masters_WCC.pgn.bz2 b/share/doc/networkx-1.11/examples/drawing/chess_masters_WCC.pgn.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..3761ce5280c7a4906b04fab66d422cf92ab567be GIT binary patch literal 100224 zcmZU4RZtvSuq`3D1sPm|I|O$K?(XgmGq^(%+}+(}a2*1J1a}B7!{82s1WSON^Ul4m z>ixW3)m5u|ckPc}yVg&4%bGfi^2(Sn>zSAdeH1{h@8A7~L}>is--E$_|Ly(!@9wQ_ zgp?T4Et)jaALQcqbm|N@wLf8ex)}=kgcrAXNHl$`2AS^+TLsV*^U;#AP}O7%%2py( zCn;RacET1oF5THRP)Wv+%^V1YMoa;V^{9<(_&~D&@bfO9KK|*w2Csq+tpY8WkiPDn z!Sm>A&w{Tw@Z=BHRtQEAhge{aH_e-@G3RBM2e*+*r zaK*@~kss^$`FBv5WTi`|(d%iLX|EV>8P4ft>o`q(uyEQH@l_K71M}bc9!c!f4qV@~ ze$?`J7D_qQ7lR0H47~K%+}C7XKfZfj7Zcn@0*IwsY`ktE)r40A$^=wGg!&|Y5$m&C zZtdDNT#jUQA%8?)UF`LMniwJaiO~4>8!l*(H}Yi$k9E>Q zE*)BS6<3&~j;gQlIV5Y@bXj*-)~D7dfga+Ukn8(n>eFED2HPcIG60QV3pyeLxrqQg z@Dk$dVpIde-EFsk3T!nfgl1s^zOsZm$O2Ww_mYTD)SxW*<@J42zWVGGm^N-%V<(!Q z+)^qb>{A2URXO26s{tk;#6}glyA|yTrw<8>j+I=1%brz(PT+M%;BfMO9eT<2(HPN{ z-Xu%n9{N&0^s5?!7@tXPV9$$~PW2G+lZp1Z`GVcf{+qT@2Yv670#FLP?vPu*3D@kY z=*($g=MZYxr77(HXpG8bTlP~vO1{3DJ++YQ+3&d* z5@MEq?=K>*3t?V)?N<;Nu znB}VpYrw}eI!CSoNJtd_m3#STJQE{^N_<%v`hPSyBfYE5V60LBgLxEJ9+CK*|0e}m zgC3wmgrQq{e!*IRg!ApckgRT0UI9gcH9(`H1oK;YyGx}WpTH7^DkB?KX|`}Z&W@gP z%r^=i403V`TLl^Eudb}|herS!GfFpuh)8$aWzEiB3t|GaRZ90u>s2&sm|$B+TSWb0 zav?b#Mw~1@uTJ8(q0v!e1w#(9q(Vg;_XDi8S$)$++oqqku8jaJsoBs;N~##*3MSVhL0fTwyMwi;7!mtgIHq zZFa^9YYE9^Gfik*h8xLU*2}InD>4*aVpmW70Y$IeDn9}88%H$W)F7EBC9;qkcURXh zlZLE8pIEWq`NjU7XcQSq9bL(cH#z+XE*T^TYiNv(7FwSTt&x|Gr^!b6gw>WU^ zdmNOhR74K678%#1*PH3$Psrx04nJV@t3eb*vYGi!XQeq%^-LRsY?Sok?gKRY_IbcvzYR>M!8gcA+sIo0Z?1Z_x9{`u=kjH71S6ggO!>QLME&r5jZQC=Qx+5y0N0sI-~0RMN% zpD*9Pg_=k)K7phMRP3}X!v0AA(BB zBtOJRpu)17`I$w3R@&#B>IlDcRz7q>IDkn}T9+rxzA+A&QRSJtj7nu(U0%k$lrK|=d;E&d=skqQUoLfKyn z_F{RZDdy59)B1RNJl7#if>}vUEByQ*1YQpcKC%8WlL89K&$@(I?Xmypn&q5dsYmKBrLcR;3-5hf<#+bBSP_#q{b5()Kny!~&>?{^4g4xZWxLoegZased z<468l!bo~2kW441EpJJ6UUu3&7OQCp*TB@3JEuaC6Ne*f5l zjyfsBtFX}ol zAwkoNFIB@Pdm3~}D3Xo);pEO{fcB{pOnMqGM^-?Px#ifY^jNJ2|2q9yl7RDrZZasq zx2}~x`8N$J6`H};+G`*A-_O{6v8Tg21sv?D7SqG13ULN7?|-iM+=8Y8tuSxo3#cyP zMkThii-I4aBDK`ex{#U)%gPow)5T4VLW`KlGR>VP(d{zz36Tgxh9L80*QJ~$9l?uh zeUo|M&NTbeEBw&7mwne`H-x01kTjP*XL(+(I&$T*xaUr>Jap~4-+EbyWF{3)=`0HS zz(6izEZL6}!=dSZ0*&mMm#eiwtSiE^JJui4`7H1mXOk;b_I_3MzUc8ze(c1f`PcKn z)9(kFIrM>*8c6Jy{B^EPKjSg(76+ARhVlcO!aM4OtSAmkXKdsk`#j^g3>~TgMK8=q zF@6Ds9KmcXz@N2$Ue6zX57Z5+DzRrFu(g_BifzS+7hDQ{$(hmB_|IQwd?sl~8B}fI zs{?`VLEa&i#6vMZ+v2`61`w^_7vCX#Z%iEN0mdVuqzGM^xMvi=*!h#e zr@+)`LN?Qe1$=sJSf}m5uSnhL?60~UY?6ihE+0gA3F0QuTB5CzE@=LSg*2cmAIO>d z-^T`D%Non(=*e;a9XQ69b5Yg1hsfBBi1YZWhsF&ei#poK+$iRVY#O)|A``|QBa1lt z^3xQru7;cr06h_{w8F^-MkAw5!VtKJ@{+NR^I85eGa6Cx`=JM zWxvOnisHe$7%W_YrKr|>+WU9uB2JQV%X)6APUOEu@}g7y-R&hEaO(CGo`%zV8=8Inv8{7@2qB;S1tH+sf)MEh$iV zI&h5^Q{58&DWx_Hq6s=%vMxNJ$3HQ%%xGeP4}`c)?x{Jx?+Jf1i+Vd|4Pmi%(EA=e zCahwnIfDkrZ#)IF-t&Ac0Z`f@y)}i@pJQ}6GZ|nNCM*0ID7>tA6r^xCd6iAu@Bw(< z#q5D}=oisA;SeFLP8KS|p)l~7Wj+qrO*rFOT&vqEWaF)rzp1A9^BYRW*NY6rclfjl zUju3Rs#qiNzkR%}!&i+^u79pWM}7Y{`aK333JwE1BXSxKPxv?eZ=N{utk$UNtoE{m zx&UQmBql~QX=^exLRpzKYcvB|rf`4*G9e*B1}d$tHX|~U4Vrv9EiIi=J!?E>)SaXy z4QZ;R)ITLgn$v=2fAY4H|E=#f`B$!h3mbnfhOGTsfpV<+{jU;@doPptPY~R%7$O-x zpWZC*h!zc=gM>1YD6dAh*~!-WzR7^71hP$d3M|cajDn$+jd}lg`_9%l4yKL9BHy`h zVF&Q=*SvvH@qHr&Ax6$bqHq)z78zHQ1(B0|R&_sz%SGBZ8cm z$<(tw6OCG65uV5GPpC$sZ5$xfJNJif4$`NcMEkeE!BwXQgiqJpWpgSLA~oY_1@P`viP)yB2XZgC}7xmmM(G_E9|pAmK^ zRO+$S)~Q|H|}+A>%WRc{47(OjS)5&?n?N zg)TqRc4{cSr!5t@HI0mzC+4*sf^k-%m60ZSo^nuBd+r+rnY}8ygZRFX6tQhnrE*!(pxkwmMopmOLM7J z(I?{cf5Up$)_JW~H`(kKsasl3U3K$4YY}EBT1y$Hf<#s7Qi!uIkuo4Ds^(b|n ztJYJxuU94Xy~C%=5HLb<=Q5S0WY}uZWglJ6aCoQsR{51)waDyrVxN3y>Da+ZRM&@jv zcgFmBHqc)(qwc`AteC48v(HGV`!&JK085)T;M7oeUAHm1PTb84%+$Hu{@IrukJsm@ zRnhD0j1f_#w6zE%ZnDrY#_XGhDdd|MQ)w<7*BjgA)0^o5@DJemwhi(R z1<`8s$rpwG16r9ZzH=O(CJm$W z3!82}r|v>;InEhVoWknAtYPpRPrsF@8!4T3cWH z*WalY7ZR1Rua|NmLYXm!hfP0@<%56O)qg5wZPsb}#|YZQ&G)vto;oLSN!KJ$h9g>o zt%6tq3<=`EvUEDPo&~G*h_;Hri7+X@{R0u8Uz(t| zj-TCr0l64>0Dlg1Bo~7$_c}+l#mC+4V^WYGyI3aMOLcNaa?M}*;r^yGtP1Lx^L50j z(|5)~b}4&RJ>}XCZpTkuHBAXc>)C2PVjIJ5KwiSh;Bpp^evRVQ1oICj?u?{CT-WD) zqaD{d4^}*(IflE(@t+26L^p4|FD-17SH{SOk0~Ja4N0I#!!Al)m2_3GEqPpIxH}V{ zyZ}y609Y{K{aP zwg%fj@5~%ysY~q_3SK_P$S${Z=0O|pXpH!PAW;dRN8-Y3@-LK|;PxAMtr9@UnL1D=Pek9$!@tDmR zPF#btYV3!(T*%1&HFb%!gRUXwK1O>RlrfcrSpJtHf#+LoZOkN%aQiI347$PYuhg^s zQ&%(H)SFyu9D24?@$iBQatK?el@IrbzP?vY9V;zQDq|V5HT2b!*AM?3MV&9uGX(niyZ0li=Ta%-M@N#k#boRqP5n= z-CCQTzjC&KlBW2=Yft^JXtk3V*dfN3OxendUzpb;lM?T}2F{vJiZ-o=GW|he0a8Ea zjx`iuQ9v{DL_K!&l#Mb>NR#TukNV)I7~n=F$)&fvS;j+vtw#XF+cE7#s4AC&p+ty_ zA<9-sRF_>ZnzEW6)TWAPGK!Kda3?ors(N)AfFZi93`@GDG>p5(==yst<9~@1Dk^U= znQpr#{iR&VKN^U(4nSx(%eKZkZ?)YrN(_cYkvx3c;+!C4%y%|bvUo+pN?(aoVZTP=pI z%@gnhVGITvfHB6wbCk*T$69M=-$E#oC1U(sA!aTXGUn}K#sj{NtLT=6v`X%{+ZlAC zy^WXb6m2X?Tissuu*HwqExihQ>rA11=?;udf5C538g8~M1W>ut!^m3YtK10dK3ON8 zSOo5zlA22kK~#h#U`D9j&f}DwIEHZi3o&M!pNU;5@HdJ5=$Vyfozy}*!WfkJW#X4e z`q3#Y7q>4u(Lj)}$R?XPtWf*gn@U`@KE0E>HZvV4fsjtajQ)JRHii{yp!0i0u>hEZ zcJ5v}tA>iA&k)}B=-H%N!MZ{mKY7XI{|3?sUe)YTK!zC5q^)aFarpW7Etcc%_N^m+ z9}z42S-L_PFTNOJkfuY;@9wTI#gvZePTN!~#Wpl*R$x6`IYQEQE_F$^m8}?T4uI(r z!{Q-c%?4M2b!dC7g;bxKm?f3y-SLkD>iTZuA6qUTw*H|$qdSLz_=v-Lr~f1ze@*~rKk)2cizTGzz06^15C?gPS3+E*-?*>y z&JV@*#)V9jeE8@VVzV1an(Dn(Ea&RQM`$seDM-Nl!P(j?e zf`=5z3@ih$=(^wfMwGlT%u~?II7L5v*b%mYo9a`=JTG}8KJO145%CeeCB(#=LZu_H zX4TCyz*p5y*EhiA(k+KZsgZ3 zVvT$J($t`kPDMYxDcub$8x2Mef!uI#ci)a%n%?KEDY=PtX%Ac)9248?gL}HpJlaPA z1^4cbIR{p3`XZi=f^{w?{?zo08Olbv&9ZiaJ#>^>lH?wyra4~J8Ee6KfQ_(?$Tkf3&z}C&yAy8DTF7#g> zJNRo`jNA1Gq+tNr+Av-P=6{ao%+iA__nE*R2eRlJN|eN2jpMqK6?z~Rp?AT|sTOJa zd`H;{4dM;QWoiRu-#7CltgGga?8YgWQ#jRaLwNX7QwxLI7e z#NfgWn}onZ4FQgr8BuXb=`#`gE+!QMZKl%|b&*l5gNeLuNl&CkXTK(w>brv=JM z^M5T(Axs@x&%arn|4nv%_wI`NqFl$QKKhkrF-I^8)+r>}C;O#@(_nKY`a$b^cPWhr zcY0v3tZiK8sPkP*$k3`nvy=ITD8%Z!x;k^$9p`qV z{^HygEwy{VYB|q+Vd|Avn)Vm5jc{J{3Is^%QtK41!5sQT_(IxB^Ei`SXXVTHo@qz?uMAIB1|qR1`Xe> zS%8Nr{>^^6)>1 zMC^E3pmlR&dR`6jp)Gox#s~xC@%2`S$#ejO^K5io z6Wgolh$(u9e#&M4{+e&a6Y#gOL|DrP+W`hR#leD0rOZ$*Y?2fRbe9!}34gJ25DHGq zW@o0(#OlJ`e$y{QeAQ~l%vX(b_{NS)$U5Lks@D>Lc5Bwvlxx^jsCUj}-Q(@rTv$zi zS=G`$20>`ljZySnLuNNz>MtDAOSn`MsLqWObDNt=OjqG4VHUq$M6H70mzo{{nz&~g zbS7If%N)qVmSNgO-TU+7YG@1xNpgeB z+2rtR(fEHq7Bj@Okj4#SmE02OQ&_)^ZB}UODi4#xfZdC7Hb6SJd2fgGCd&YXOkITqnf)DroTE~y(I>tAvZi+F7?MqH7KirXBwoL zVtU6p2)GKaWCc7l2<|lWa#bru6|wVG4_2Q)UGfvuk=$K0`Xp2Xug`z)?03QMWI$jB zvo2_W#;IQ%tB=pOw{e?R&Y>d1ypPr`%2CV)?K}E(Mf%CgXN*sq%T@GWA`8H!OJ`sDWDt6b%!ybE#g2tGXE za`2azFu*l(%PKlk6DXlrrP{SD@QpK`PR(IrFbl{1%PHt%3Cq3Ye2m(jPqPv9A_QV= zq)$hYrBBL`X+VnCia!}vZ*=~&vWz}?s9l8p?j7c}jw<_ZSNNm`5I#lwIhEBgjNMRu zB-E)MLLZ-yLW`)Hf)}6O1m^Cjv~tbhrK;%q+Rn7uzz*Td!J;X?u(NMJ8X{D_m9g4~ zr`6~(6&RD|^L3*9V65KsrlMU0T3d)&XQ+T_ab)|(kxLkB8G{5Bq@aG7lH^GW;a4kPO3jBTRKM=fviyA2f?Mx%5naN z4=R+IQpA1h@keDv9ACqj&U!T}Rb9$TWZ-ZEXWTH!^_x7dr@t2J(GUgR3f77)fjwp; zw0*F^Ej2e)RC=ZmE__L`U_O<7+x?Tucb|m*Tvw>!oSa@kw%`^k7y~S}>{lkZv6}vo zoW&Vx9j;6jr^28@xMDa}{u5Ktf+i-XHf=UuDCI z$gK18)^XB|1HA39BC}hQcn=42n0BE8kCNG4Up&lLhV|nX)u{LM6FVD4C&x3C1RYXjaL7=X%Dnd_N<$&V_)tpR>l21q0U)CZDbI8mN>j zP2D|0^xXPtG#eb5=1NAxtn$H(N`DC7gCBpCLWQLUv%m`1hwQU|t`NT~>*b-ZnSth~ z*qv1N(}wN-n^(>^puEDNQ6f(ux%j1saDAX2OgvsY3*H_egxKGv?~`f}4bL zFtcgvffI-81zRit&3hEsP+O1N1ZPWOH`IHzo$Zi!Wz9^x5I zbKxt|BM@NC(N_%c$9*qDh-dCLl8!8LI9k%WX{@S8ne03JzP7X~V{d>yai_cC(V}i3 z=h7osXLzTf8ehXnj)T7>oI>Q8{j+Nx>9??6*OTANmEv8}0Ulxo#=jZ01Gx|lPQ?N> z*;0j)=0T4|#Vg#n2@2OluG3wQKD|3GGwlArD1RG4%rpf(%L;L5FJc7F5Jnnk-)MJn zpcbcJWW4labGbRIAkV@kozqusgkZTh#VydC^^Et>^xnF*0G?m!!smW&S*(~VD7IR` zNXWs<&_qsG?_5-~BJitIs!w01ADqJ=;r8@nKe9mJ6fjT2p%*%mNP!bIjg+QNxiQb6 zD~}$p7ji+fhAm=-=wMOcc$&TFw;A5fZ`Uf`r5Eyy$4;_C`{gpzd?!uU=E9DDuS4>E z*X0i)-pb0g(VgrNt-vMBRDbH6cP-_?N@!%iL44yR zra?c9Xt?>}OH`@kbOM7Wu&2V!VPJNc&tq@H{YQe!{dJZPmUc?4YWDX#JK zLd;J!D^%ov5=CQadi@Cel5EPfDAEM34Q8$vwj*t+E>u~KUz_NAQ(ZPaTi}SlFg;bt zJRasGpwwZkj()r2v7Y-bZYGc1+H8N%!cmK;Kw0JX zv|+SiC^W^A+R?e_Tn$hV%7{u&BUtwReyUiNo99~BJNwMWGjExBQRRxLO0ti#Y=Ppa zV#NF?IccYo^Qqh(=LCDM7qIEjiS8GBa1hGj&U)M=0M9U0_WzT?;PYqMH|$=3)3ytsj^*H(pxcck~I;l|InhUQ@|O@mQ{6}xl9#(Pz$X# z{ds7}XN2)*hfH`Rn3*~3o&qGlzf_p84D z);CdQi^0Ywy0NpYW^7^>i3=8p&p8Pr#17}`X%brZeR0~_ywWlfuH^ha#z66!?LN5? z*veGKY15V?Ld#F;w<%?SY-#k2F(#YdGZQySG^(s+DTX`N>^k|yLC_w*b-(E^i9hNh z-t`qxh-Z2@XZfWYYO3LuK}E1&bAe07Yk9TcXq9;*8E?Y55*JR=c~wz8I!Wk04=C0? z*UWnk#_ASR6zcJzuMP#IQ*R3vzKhCP<7qx|@F69+EFeDmeY0p2CK7tHU4Bq`d-4Gz zF3*;7Ni2~JImVS06Vn+_r6w&R4o`uq1ds-Z)F+FSqa*mv9N3r4kggH6|IWN>PgTbq zPrn<4LbUmFZ!0Gfic-}(^&NzR&YWSW9G6i~AU{3AEr2#$&6~zS=DVBrM=OWZ$3rj7 zgWk<3E#B6TzQYxjDUL5=98E6n2n#3*L;m2)PNx*qT&~w6f_sd~Z z%v10C#Y!%ZXBn_{>RS!pB4Z+Vr!6xfmPE|SVipxQ3s<@xld%R7)qV?D6eHxPGaRoo z>*C}}99S+S5zcrYEG3dYMoz7>QOXT{YG*Y!doQASkknt<45U<0$00LjLndQd%)w#C z8KuooX)V$dZg;${F=9&H%JcK`6sNdyaE;P(@Bq48q%Z!_yzG7NIRr34Roh1VEJ z2`$#u)ada#G#B7NSnb4e6nx|wZ0A-={+3nvGx)`Fgrso6+&vSHB=eZ%-DeXS5SPs6 zuXvK`4nBl&y7^R)yZX!31|y~JCbsM?+`p2=8al|`%F*<_00&7` z+}6P6f9h|%KAFIG6xtEV?#+gI>H1ZrsRo`{-PGeTKF(1yeX)s&BN2(N4{M@`kgqRO zvH$vhf)|>OUK?up@iPIHp?&eM^Z=&zG+lZUEaRn^S)c?gVoR zXC2a*o2A*&X`OY*RyOcV&hx;pS4WlzR+x6GbiQb**8uyZ#;ezyC%q--tM=`Ap8ja&i;Ek&#?y6L?n9R=is#a>VukB%dpR(urAXQg8j1L< zDvaL)4cCcc*%4dtPM&Nup&15aHt);nsn#OPA4*b2ly-?%oN0NH(xYLyHQ}g+@i|g) zMdTRC_$R%xb>7>>intYhQh}HXTs?mdy&o7~ZVvNbsv{r>^L12c-?dB(?UQ-Ue0hoW z(ofp4)6sUfiTof~Rid!6v$w|YI_n&97vLw+=EjC|+`*4-`;|vw(muK^m3~Osn`c_n zQM+Q?(~x7PIzZ@f?!%!WW!v;XyYFn=Soj+jJ5!$i`g;F>V_L90dMZA<)nAjvhY`3OY2aGkrU?t5Iy*a+Vm~@f4AgP{m&{lzF)iwt` zImCv+ud%rE>8Vl(i^<9RbjFf#s0Rc1&jiCY~di9PR83>Y}b=*B8?}L{K=C5AoK8OF_TeBNvsZaGw5azQhTTtrU;96g{9|2v2v1K{UPK@PsZDOi82kp zGxm3R+59d?ow}b{5d;wU8(9%r&YWH}FpPSz$5Wwp{K1bTD(4~TmnI9|{AqB!3+pL| zvSA#opD~+`0O=RdXKVG#hK3FTe`gg`(=XEN>i2}*uY6gZA__*1z&Agb5~$ymp!_|B z4^SGmzk3dwzNh&c)(6bDVpi_j7Gc0o@;I?1keUlnRN_wVWcCN-Yb)O)>nDuOll$*{ z^UTy<-(L!wu|d==B-_@8vp1Emn#_rD zPo$*KWpRdTGw(T+m+g%63RiN8{IsCS)CeU-zU?q_1!O2YrQqMGvQ8vFzb_!!z?cBL z=M@r=S@H6T3>4Z#<-hHaJBC8hB|Ou^@=pk2U3r047{ff3I`pp8_`_OKB-<1O2gbZn z^FQ2d>}Nk{9OL;P>$vh$$tcpuo3J9@qbSEPCQG)#2Tkh-Yyq2}q8{da8+VBnT$VvZ zDcbH?6CNPBC4u9? zS59$W6(oax=g+DBJJ>E{4W6f1?3Mb=h4mOoRAcF0>T~3SE+T1>oJn?)Fn{W@i+$Ax zgRY90P?xQ@zD}($RE3e-DLN9tRnjW$eR9xfx8jhDFQ;^$rV=$d)jSk;BJC-?d6qo^uJ z*$nN;a_vk8785s|@MOarX5367dbbq(a!v)RDyOdN(kybsP1y%$0rKGQ zs_u>F{e6QB6fdo2neQ^x1egmw;7#F^#gpc8mP$Z zP003jqcsC9IrQ<9^<^ka9md58BnnT6wD^x6p@0r0$MmfZ^+|JAL5&t92_PCvFt+p4h*kNyu>g$6CgA6= zZ%ZFPo-kH7kucM#`J9?IYoUzJ zZ(DLrNCZr1ru)5p!$3srJZ1-_X=i8?`{Id;nrOp>_B#nVD)~n0doHEmS>t71+Jt^5 zg|=W~Eq$t=7N;}OZ(nd!SikA$-{$(xMuVGyhH%FY0y*kQ&%OKdf){8dR1RewoURpgsdj@6&c0fWZ$ibvvN> zI)STq1Z7drlgnYPSJ?Cw4@PQq^dd7PO0XU1sb<4^;AW%abp5pALj&h@RP7O1tRuy0 zV=qqSPY=G_{F9p_B%PR=u?#Q%oK*O|`X2hTfk+_F{O?}=l&j9>n5VUMD$VOx9UJ}1 zjEszkp&jv*j&2?wc=cmI(4P#KVt|ChXNG5{y@S|Kq8i5mcON=@@^ALJLq24WYFNbn zt#`{sG314yq-A#XY1LU-+@pC+?CCV6@g z>XTDKzY%vV9I~Xzyi_e5D}OgJ)E3o_rQx`tX@g;IKEp#>&PkT*0JJ}{GGul zG*xKK&}HB}!PEYttbbNJp_Wz&QSI<>m=ECZ!d=cI`{7V%9S`a;S##}dZyD!RgHFT} z#nx>qS^4imcX^k>LVjoHJRt(<4W~KulXw+NZ*>{oeR&nP*_OcjV4y&dEVQaCz?u@c zD1_+%Hl}*9J)=>3zSH!T{{y#qEX*dzT@MJq4|(U7-`mu-51^!Fs8(oZ8+S6HXIo-jie-i4xYD>RhCGG^$Q13{U(olBe<%+p= zr2xQ2@p+T?@!#(YrclX;Gb$@uK)a`bPp>lz3~!m-vR8JqU8RN30p82u0XY80$Ho04N>13>q)v9BYy8s1BLKLSfB(e4Ia%n>bJUS$ra= zauyt>E!s6gd$m!K;ts9j%STbLrL?3N+&e!A@~*iEcKYoDZl8D)l)C7oyqW*Anr;tZ zo{V{5rfn~3n`3U?aIk9)Skf7*?^?3pk3zsRLRX=2d&mr6^sV1{? zwAgIMKVa78UfCtrS?3$;E?tgJ1=p4j`>ujZyM!2iggml4;+&WZiep4FshD6PA77FTE}Zhg$^{{O-Ee#7~s3w&+~VCFKX#(!z2hwFiq|8DO*Ex?Zis|Tw%~Lx$Zzbj(It6!$;Bl(39hZC|5Vppph)Kv z91?Um#d#?gDTteGy~=I1;w|sCGsLGWjQp}g-rU>8#Z%! z{h8bo&(D6Hmh5C53;%^_J7^ZoFCIR8qKhwoXD^|>_4H3wT~zuPpM1Py=CNM4VZQhZ zp#d)kat^%m?C9Ph|4SYaxl7~K(+ZYj|{t=pbzbml)7=l23d z+<~8dv;87X`QrcIn7jx9hO|!AIRO2#W}I@+_X>r^RGIGXBPeM6Z5c}A^PZOZM=C_x z1`27=t{|Y;anp#;PR{EQ%Fer&nz?$Q4A6AA@{m%e%U@PUPfh^!qm<8}q>k#nE6(Lv zh(|eHlpIZ}W75S%p~h**E59;g5PRM!3g~raURnEz7|676vLTTALPTE)6ktY%=|^t@ zsy%a<&CCq!v)LV0=ZM~u2vc8tFJ$)0_-_I1ii;IPQMYdT>`O=^!%oegp7b>br7ZSp zyz^BJonVAa)AN$sy-F|WS@DF8gN3OKv^6WeA)s^(%hDiO`7_HrE^Zj6Mljde^9d$= z(cf-cFYr2O;#*_e-070xda(=vUoQlpqUI)VF+h4+VvM;*}& zJ;e)0WmW+@48XNp&D#Ig0u1mR)vYV0O_}~=ZnyXEaiR)}#WnuEvC4QOO2xBVKK?MM z{^F%_9EoaIMJ`xFr}@|QWsEd)Vnimr?%74#@-93nu2Gks;hk3OtW}BT zuJyM1Z4z>=YFl{dk|8-!|Z(h9jS>ryZ9t@e_XlMK4^RE}( zXW5SjSo9&^KAe@^8E9E&46Z7_`=`Q_ypG=b3MUdpn`gbBs4mt32Yu9--u_4U<>O1g z!IODm_2sFu>8EG@4^Q?Fs);*@#^u$|pH=qX3;WG*{p)?aNxWN=o3^>V$2-=95gV~Q z?Ee1H>k0jN{_Vwa`+&j4>NA-T+=^%d5SVKt|6^qcOXcr%CLKINwsy{qph&F5hLpi1 zwPXsy!T5^ZTfpFmom@sLT)5dPdv4M$BSwn4J%w#*c9g{;FZe|YGGp>SzCdUuwQ`(N zpJ26GUH@suaDm{@@$CipJSbkQvnuwKzgvQTKdRrJNpP@;H&Hat90A8)8~)C(=u3*G z_H1A%G30?Qzwp^iRCcDQ!RT~xe5x-XS0XhzcvP^K)kyu(gJ%bdZc`b=8B|oRoKMkh z=DLt~y&D%QIRHYFYdL*b5stvQPz%rhIgfYi{w~VqCoy@hKT3jYu4X^U$XfHsZa@1i z0jb_n2Sc8}Fd zagx_p9l<~*fn7o1Zk5zmvhv~Q(vfC0AqKi`P}zz72eiQoz}50y`;`Xk?aEetl3WNY z10=R_oIILvoqK}{w1BX*d##XE)Ol{b_gT&9WsTc^hf#Wa$0!?^yr( z@2^<1u9?|PI3mQq#i!)VfUpGJx1uy6G0zv>6-sh+@(vfG+1+-18Z}h&8rBS2O}kI( z#!xjR=WHO{yp~_YySRz6O_U211LNb*~fL*30TIY5?3mZ0aLHC!Pme8NJFB_7NbWnrl1ck-K;xr34 z-pwx*B()E(IrBH5Wi!`&pnHsqwElmjlEu@SwFFj6{VoXmz_A5z)7jhOA;s2Vf?mhlLxx=uFyZ6#(xKc zAL6G%hWl7*iku3~y%M7-onyf_`V|7xpf1fwrJ@X~c@59_@_77Iea@o{>lA@i)+mhV zpP8mlwh~2*MJ6YGNKEQ60bUfF#7WiU672R(1?+DJlKxGER*H$|_kjcWfBB=$(84HU zc;+HAqfxDDrf=WR{nJkhbE|Sn$H+?%y&t3$6e=yVC3xWZri_y0;i;49UGO$OHlfjw zF7SJx$Miz80{bL3N+D^el%gk@A)Fyie*Jkq`%WST{v8%L9rB)0lK5@FoX-HkP3;@7)5o+Y*=->WWDfM77pCE672q-`}EM-l?)z z30%IjVsSV9=5i&$nZSzRcb&TiacpGA1U`DSdf~OIj>5!1sRMpjf3!Em9;fb4%To6X}NDYWfwhCS4FC}R*0R~G-kxqHpLRITMSfC>aU8r z@F;>s_u7`6CDI1~UBBLUZ9SD&Ojyga2Y**dJ7(PAiFuKg#LZiq#m-MA_e%*7hCXqu zq!l6wtVGl>O3o!!+iTa|_sipmCYQ&_$U?U@+l|Z~1nR8?iwanN1C?7HaaYPhsTRWZ zyqulSrR?hYtPD^|%BMR7A|Ui@!XN_z3+(n6D6F%g&1q^-K%t;)-D%3gw?|_k2cr;i zOejb8;!UDvF1$wW@#%%zVyxXwLu+)k*BYkM-BentZ*{HZ!?@(pn>(VvJA6)d+h*<8 zXttE`whk~BB$8rDX^3gIXbYLtqaj^DS~KCndbv_?mns2 zvVGp_h!2p_#ASE$w^n`CErtE8vElNqtiwqwYe-cvRfdHk!uqg z-t6VCA2XG9RO%$KQVC*6^OzP1<|ZQ)Y!rqlb>qXdND%TrHW(lveX%7!NjK?UVAWm; zaw`ah;VJ6~SmG}O%EU|=j$`wN;tBQg*B^+15(%j{ozJI9S~|^)YP)30+?QOs@D$e6 zrBUeJeXFc6Hh_ zb=pbNUB7I-n!0GALnfo?B!6Lr8P!5@xKY8O%wDRTiXh^jTXVXB4^LJzW|SOdva4ms zY@(^E%G(Vz-F#Z?5K)zlEoJgj0`xwb*E`arFwh*+Yiq~JYfF2!<;n_su%8eq#lz>Fc zAVSJ0ly%E)X;!22(zG|0M&Hqf{bnkPkWrBl6cG+f^D3fOCcwCsl{jR?KnDp-91zE~XHF>V?07i`oLePmcnoMUF*pb-AO5OTh^4&ID)K|oNdJNRI(e958{W@NEs5^jnG{YhNDS2xz0NXKuPUi1$ZiYI@p$(rriUn!2qMZeFg2#KOnB zuQa+STu^QN%hRL9ogt*F|XT3F+@=jEN+ z+=-lI612dYO1oVwo>pb7*Rm?oV5!M`oY?m6@pKxAt(TR&9j+$RXBKNaf&j*|L!t~eH&8aauy(90Of5#++k2%tZ&!Pr zk4r0(GHi^^j;38r5##5M_O8Wx_Mu|mB+=-T2yx~fGi*b%-lhx?VmcF`>>3hbXB;@s z5jSM;yK+5abdJE2YUD#B)LAX8r7~$^+gzgx0(87t&xm68%s)of-k?TF4(mE+^ zP0G_M91terj(W^S+j#DdY+Td~`R0p@2A`Bv$t+dAlXklq?OV?ir3zR?xw zZF{?F5~-i_*p{}tx^+p-gONQ2M9A7tw7@S zl#SgU^`6m4K(sgFYbj|NO)SeauPlPp-m;k6TKghDV+tV1c^fH@iYC?ARIaCX-D`@i ze2Zey+JZ&(u{x9Wa6l+t(WXs*HMKOf+gDs3=$%E3erbyKwFUIIKJT~NQ&a@ZcDp6A zBBJ}#i@g=Gb!dp`>%e_8w4~-Vajr1HMyTEjJIPBPo~L(?F6VchNNv=x;C!nJQTrZg zn2W-f(u!8hXXzmvM!E`MwhR$wNOr-~g|?OJ)mI7O+H8Wd#a~yp?{ss;P3_5pS1pU? zhBPBsvkDY77H%(WJL#`>dZhMiQL~toV(F#U)*H6iw7Yg$UAwN{xWb(pij|g1wL=bW z=<|ne??1Wr`_F`5AsZ^%b)l;f5nq>Qcr&^dT!xh9#?V4F!Ftgu>t$rqoy@ahvRA}($TRO%! zBF0?z^srW z>2{#X`fRP6EABTrj_agkkMDM)nY}gw&_(vEHI=6=yJc#+S;#6A4cFZ0Jx{9cL(UL` z5rrdKX3=tlt64EcTWY2$BB$*q8BcGu0}T5$7HwR*jJw;y%3 z@+Nt`y1KMXes-@LlsIYwZW>BSCz(B7Uu&DQYI^J8TYNEE=!(sIX*9^i)me2f-FJoM z?`=)UsHJ)s;?kKX=Tz64s%TcaV~DOz-L?eSDIKaNdK$>FhKR^61hPb42-Z)Iu-H-7 zu_r~jAqrV?n}lZ}cInHetT_H`pJdv7J40AUEo)oXbKRqQ^ix`W)>_qF&A#fQ+M%uu zu!~Y~dV15Qz}H-SyH&eh>>MVNhnivzd~QaQZEaygzS}2Q)kIE!p*ae=$9an@5d2vvv_!D};1i4mP0 zS9d^1>Bd&K^t{c#5{GtoK%F`f!#1hKyL#&D(s8aLRvJB9(UQ)swAv+`V)oANZ66of z+sIviT^>?scB8PaRMQ=#Z9F5WEKbQ|i$#A}y8Ru~%c_EDtzG2q?<)`xw$|NDiIQ-v zcS@s{T%rUt)KRL2|4#3CC`K+3Vv$sNsph(DdbyctBJip)G~Fr2$pMrl*#^=>7HVS# z5Vs44p?a5X*&d0C9*u8Kq=J(m^}{4F*b{-M-aC+H0-AlRG3}@ojacM5R=tPhwV-N` zGWBwY86l8gG|AP8PEeBYm~Ed^_e{*=S`&E@t_+w!rrXWQVR`MNYTBfb*P>e@X{d&i zd!?d1WFz|Ljy-Jmn}k-!a`d*~2IU7$%}8l!2N9P>T=hY=s-30oxy7Q|ORj^clXTXf zs2H51GV)W;b_kAZc@8GNT2pkT3L^$74vLuFO0=(7;!Y2RMHR^t-_ji6tsTNWLH72! zd|Av0Z?g^cc=kkn^YEi_40hy67?>T%V7EcdScoNpaIS;ao~}5k#BROmy0>j+t~3_w z69zL}UV3F)sAz%Oni?u3*dhsLotev=LE_uG{oL;KPN6s;9bal|#~vIhH+D$&CL*v} zw;5FRw$B}0c3qXj@_JRvU9BPT{r&M`+=B1lV>>w2Iq9&`sFVxV%(FLbsavkJY98LE zCTqO&jH_bowCXOEhguW9NY`-KQ1RwIAvhIN%1gd@Tr@o1xZ}f zPoINp&$@~+ibdhC&z9N%{_z&<7uucIA9ZXHjjkX_maZYxHqx(E;NjXBn(;H7Bgz_+ z5L&H@U@NaOiZ{XVl{3|^3r!cvVx}^&R24-f--p3IIi)u~a%-WXDA<)d=Qz_RG@c%U zS}bNj`RBr&<(;kTvJ&q*AZc@BFUNe)l)dur@2pw z=gHpBC1pB_BKlER@z1>P5crtMIps3U`z(S)f+EvKBf}_1>peG0kmcmcA$^pvl9ni= z`1cL>^WYKxRS#JEszM@)7MLU6q(C#3BC5tTX|Uop2#O$r+iDXqnJ6$3G!XO!7tKS% z`+slp|Dh*K{#;flYJcXlNG@8!j*Q|rWSoruT*z6*SP`+sC9}_L)aJv^Z)mRSVS6lJ z=Xw`Y#$`8DTEknKKp>#GK&T*N$|6i4xp2>$GP_jg*@-6r3Avv4{^F-Df;pWu0qKB@!Df0JO?F?uyz7*a6aPmCF?xN65Cr z92_Sg;#s&CE}4|(>RhZ%v=Ka*=xYP_WR!6vKK%`0)tBaFBFXnBc9f z3nu>Ft4`}x%QS;kUm9LJQ6%`CN?V#sRN@ZHv{xD&kQ1WP)WjY#s?mAqtfnW65VB_} z@tHGk$*Gf*(Pe9Ou}YlVVU`XSw$q+LD|V7w({V1~jwXSI%`?cG^XsrBe)os_tp z4H8OX?cN4;e;;nMBnezf%%(&J6BOgfEhcgk;#u10iGzNf9w=5M|xDwYfFzyEJJH#2V|f zUg>7)L$;R1U|S>B%$X{)U7LHYUOtuaORn|aDs6YGB0?<%BOD@|B)5L+x9=@j32BQi zIhiQZ&hBg@$yjiqP^h$c%ur&;u>3Pf)<6s(2S_w`vOTZDW<+2gpApy` zYjhap(vyNhq861)knr#d zu5%2AC7JiOZ!p2hKSeErGXX(kt1m(|CJk#^vM2dW+KMLGB-+A5)+?f8+U=TXytUv? zc;9W61&cE`UT59nd(DUvZyrHe7HOJir%f{*dSenwkoIODYN5RQlhMGOZ63GJQ`%Lx z6eG(c&W~MIHELpsppe#WCjPrh{Ju4N<>7K(7vq|1NL1o^Aes%Sys=9J!ic~iV<7>A z661%*ecUSy2_q0_5}<3=^Ud-U(wAE^WuFu3*VU!U zav40BYh496sg6~oB89dkgo`GevsT8FMZ+7W8K);XF@frkdNI?PGFEZKa?YWMd4kIM zo^Xo_-$N$o7h$u^m5``vKH@Ne3#7tmG+LGo^wSP&2Vx2ov?v$Y8rLKfRv(Z?COvy0 z*Kv|SDQfwB6$2$@<8G2$k6C95DpyJfmQ*scU6IwUn%xLyW+b8&dEq$bJwVRCcEZT{ zk^F9pv*EiK{@XL#2zDlk^q)HzXp|FQtdT*>esU`frHsub0VIPlB!DDq@oRV@Ehs`v zBuNECo{f~C-W=pXb7hDFK^)6mqA^)?rNT(z8Cwhx#g0 z@bxvq$YyBFVgSp)pr9+VhbKeQ4#@P1c!BZiLSjfZb@_(6^j=#QYbTf@$V^L21edT? z_xkSl=?E67X{Cil0C3aRQR>a$0*>sTfPVk3nBMi*#0qSD&Jd5K+5v7oBHmxF0=`PHOJ{Y0_zWaFV-KwUS znt1@HqlgujC147%QqyJD>u+a76z+Gqx$4~Ov8%f%0@1f))IJp<`bsBZjEH>8Q5&hu zQa`FqBtc?&(B%xWdb~A`qAz~6E<+C{DmR6T{8tbb)(>Ta? zJy;PuPmiI#fl)E@94Z&#c+{;+7fU{O=6d&`s{Bd8VZZk*zb;jH&6&EC3ivA)>QsjpU16$O~l2MkopP~@&PGwQc)KoBXi6(9%W?G8@D`H;P)EuosnUUt2v1^;vZ*q=q%+aRO z?1@g?Pr1#R%bLvJFIS77oY-*|7%y&k;3{f{vZ7R zJ38h3aCf-D9h_9WExU7oLD+!gQ8IJ*1K93lh5Skq?+hO=HJv?^kabT4>YvV^q%jL5 zhv50!^5L*19{;sZ{d8_LxS97dnwguE3#MDS7Ec<;su5WmemLe~VTU4@Y&uVbx?Y~7 zm;xWe&olSYZNTP&B;awJa&5!1CIiqXv`5~Af)bs9KlXtrN}jb0ukiqc{z7Fs3;tpU zwFiPSqx^wY`%yh$2cScM4~M>(zEi65Es88=nwJZ0M579+h&F=7sAB|_WmRpL0v5|F z3rGc#hFN4{%!;ath=M6=Xenus%!(obg%p6Xf;L=X2}yw9z)6ZmTV$gZG0Ac?wqOyF zgtFqBRO)obZHcXDgJV&k+S?2XB1Iz*Mi^pcgHn;L!Lg850bFIQu!5m5AxK0CNZWxI zD-mK7Ymif;L86k95fTtaLjnO7BV@R-gu!cZ2uubL7)7|!uxxTwN~8pe#;DP+-1_5Q zeMUb`-)?)5->0L`o7T6mygQ_J2aoxU=X!>GdFXWE-?K-*W%fL^Z_DM|75}q()Z;`|AFuCL9j*s6Ai?sUo?t7d>XT7;9nS&_8<8^=z&jP zsrRh=feG&?negeRb4$0WG*t{eKK9oj)ypN{3*R$?AHMo#uTIZ?N9mqZ%*xIxF*>oX z&1^lx@A-Ke-wrLY%aQQ-+-37)2R1tM>D}7x+U5U%sViUiRSMOjHq@?zI(<*oT~Gem z|A2qQKhiw+=F8iDXTt?1i2R(T{ZCF4@o(w+ZIHigFs4d9IoUi_!sMA_S7r$=Ii-h z_asrAw6+>t=HIjQ_QUnylh4Xe(jtAKe0pD*zpsAf)7-f7h|RSHf~b0^(wiDTsaCG$ z&*a53*HYN24nc^l4TmE;X|-Z=(C&2K$BvKNkB47} zccXkCxt;%tv~V&jo!Lje1+N#MyYR`Hy5QmtAA8w>_V?+B=i+ew@7hiDKE!Y~yp#RA zZOB5+uRS+^Pv4_W9ABF6!wgraPEFc)#2G^3D7|ST%_69{n(3UrdN(U0!{@l9yYiJkn2Ydd=2(Z?k;J zq`t==9QWmh8N3NHEukU*wPJ z9@!-Mb|%M|K^d%wJq{nwzihX?qrxT|{M6syk(KZ7vv}#|Blqp!b?CR6Gm$%uZisZ_ z(c$6$A}D@z2t@`^m3}~wf#cuT)8YDmf1lAm&u=Z8?{wZ+WOEi{ZQ$E|-c{M*-k$fl zJ>Pe|ocG(CFTF}LnfJZpjTr;RC)eB?KE|GXt2}YkiQVGrKGESL?{nWra+l2AD~De5 z)iC1@Gm8Db^KaOj2w7`|qms27ERx&x?%&V0-`~7_BkykePIrd=N50K|D;dX&F(sH5 z7lo_blllH}rs&$Pt@qyzjsBM#ByNPV+Uu4UTWwa|$fB9=$M3nnA>`7I%5&mF`PyTVqbo1RCv2xolDaq20 zcXf5MXyDr8k#fEknqAkj%#K2>nDgO@e1r0XEV5!P<|8088=Ez`a!Y0#u{*8RrN45F z)qd`vGg}3!`(D>;h;)&nZbHXua66&f-nlwme%osAoVCvA{kvXy-dehr&!@F+uSs_r zs8lxL!{I&Y3S1jpDBG;_zTKtuUb5Jpde3V@_a|JH%eGa%BpvFc5~0vZ)mn8#?PHFz zq7~SibFD=h*2wT7JEgl?rB)*PB^|nrUOQ9WZd*p}kx@~dcT&onwR$mZCsaAekX4Iq zqt`V@Y<2EQvj!5dYedx%H1 zLUmDei=@)o&!|-%H*xJ54O+$4oQ*Rr`h+F7 zNU;Kc2u7PwPA-*fLJ-~>)KjKBYZFiHq%yCCQAk9p8VONzrV>Xi^E(>TT-wLOa5AC| zqF{+(6d@iHMOW<69o0~P+=oF*^`!+(4N$e?R?uv77rV6;KM%Jw@}0;d6PI~Hp073c zRe0-r!f`=APg!3P9z0Cmlt7eK4U7Oqk2{s~Ewe)Vk5mT<2;o{LuUWl!QyOWJK29;a z9pa+LyVCM?cuWOTPB^1pBp0M52Ldjsovg_?ssIahy#?zrH#PfIgfQE@$-SF>MoC?4 z=vv8mc$#C$t>%eQ-u#gB%BQ*NMHXQda%lvsm((V&)LT0MIRsVi-?u z>3mhM^MSNHh~XNxD(IQ@X10%~NOF~>Ll@oCz3t1QW-}A5Qx-)>fNry*@lTvAJ7jXg za?_TQB%iNlYT4YAJp(*sISx*G^PDmqlLG?@f{HbPDt>#C>sLL$O`GdiweCc!tG48L zwni=UL$9fwcI}-t8`{~cUn!R*mP?s>=1YdNWj?L8No85}DoLBo97Y;4A_cm_nf&Ih>?F0|dC-{K>WKYhZ@l`*;Uce9bQ1^m< z&nRS%M(bo362EThnhAlHbI@npAnnGr5nJ=J z?QcGf)l`slxkiL!4^2N^A{$>EjPh!grw^&(>D=lQC)U(QreP}sO@NM>lZgdxE#Atq z3UfGcc()jc)D(Bdz(<+iK1G@yAA-6o#BgC8?Qwk|x!Ur2hn(LMD?qeCxHXFnLKo*A z1X2!TX&we+?YUzYiu*oiZpyngwz?-~{jglmbE=38Bfbog>AB65s)?5@UV;P5SYZ`P z343Ac*F53q=m+WRjL31$ zYsPO$h!*!#w)=YbQ`qwRVa{@e%NN_(h(N}UI`KcACGqTGm%1p`gm*@LnK#@$#e9TL zt=hp*Z9@x(uu@LwGG78Oy^n|^t;yQQzAG5ub(cPAEH;*r7SdbaC*CVW@hivQCA}4i zywRSN7mk8YyBT;LO6e=P-I#-Fizc3|Gj=oiUGMF?C>nM|50 zNFbmK+xHHy()aIu_M1>Md^XLkFDDX5Nu2#xtX87{rBkeT7FN*SDtE|!UX$i31o$|MTNJp4Pt1Kgo-Zk z$0BH`B7$Wh0!T$sTB$PIV4aM%{h5mGL#u1ovF)l_Y^Bzq(pmKTomDHP%vtSs#kL}p zQYT_g%V~XyV6Cbl%BO%IM#`~Bgr8Y(NKHsJV-DILDEqsq%XhU^yItdD?O(7-3lM~` z%s|2I#>g4rOSsP~udg1@u(Hw2aw8C`UMFu$sXlw1@ZP>eRaGD;swl}0dDO?}&KaL2 z=6DhjRrCT|TF2~wQ!=<_6{~4d<_~IQb??a}x1OsO5yIE%?#XN0W!;&r+U2JhywsZ3MtFIJ9h8O!|grs_`iGr@Gu~j%!qn!Had-PJsTcU zh>@Xw_#{Vca5&7oUZuI2_rOv0(OzDi@{#g-mK?CecACS-$pEfqLC(1kC?DT^IP^BS z%mHUVw=#0rgRX|_z~Cj*n*>E101S&*;9nSZorDv*E0y-=%d+6BcGyu@DJ0sj%Sm%y-k;6^+RU7 z8JyX9*b(qLm#O-Kxw|(^y8s^q;olS>BCo!AUxn=QzS|At-ZI~`P3x3u>(`2=Tt;%j z5y|o2Zn6>%Rzy>!mr&i0|6 zn-90LUiH;vz`nhUg7EdAy(qc#`{Ua)#O^cU1no0@p6^xNf%JRfp$`|{HTWz~MWTl; zJXc0*9!$Z9G+eSLuZ=6^!#Z!-=yZFYcdvNdUKyRQ=zaibufFeF^Oz$$&cshN9sA#U zB|a8F{PQ1^IJ)lp@2}p^9)sv38{Y1o_jU%ueT$&>&hKjN90yFnQ^7%bZ#)?`_b0^m z=agM}dz#D4UZ5aHhgrEs_Hhyo_khv!A9h7S_=YbXWrKOPJ%)wqvQ#6Euu zopSpH+s$&PyiZzIQ#5daURj;0eR-k3C8yei-J;wJYAu~3eD3#qPqOcnE5zC0JH)iN z#Ounw=ggavas7ViaO(MIlEpdc3m-{5J6MQ$h)HDv#PY9n9Wu{NTki?Vdj%T#YaKLI zzju-AKwo>(Nr?O2`xQ0W6Zbr?*@`pFUDvYVudF0;o|zboEwEZMcXD7pi_b0j z`wvZSiQagiM|Kgm`pN9h3Y*u4dz$r{+>&2;z1;Nky#{4Z%@nbm!Q-DED#Mwlb9b*g z{PRV8)=>1nKTOQ!np4u;__f=^Ud~&ed$jf{ymPSm3GWd%z+zqK2T@nNe0~QgNR+oX z-iG;LZuaq>N#yslH_|P6yg=VU?{B@Mv));EmsW~B*`wZwE||D8g+O&?%c93ddmid~ zsP@pDBfdDaPVv?C_ayN?8##I$hbDXHu}_^Z4e{W?y#1OV!+u-`%w~Pk<2an+#`n8= z!G?3#O$0B4Ao-6=?+GsiF=uzV@R2%}WOX)MZ4kmtf;8OG>Et&8Chxa4*a-5`Mov|s zpJ*fQyf5D&TFrUyUC0n2^yJ~JJHI~HJqa>H*!}DC=M3#GY=c6#s6$35?O zuf0Tjeb_|d^jY6d^c$ZKY5U;QermV(hljU*+3$e)hA)Om7(3iNB&W^~c}#je0nTH$ znbVuQg}e+^3a9G4j`9cF^k`?k9|mMGz}|bq+!*d(1~oqStDUY2x^B--SE10yo}{?x z(Q)S-zEFG14-0~KWx)15F5WBJeeb>7J?}4h)RQ@x=U#8Gh(7K2oU`8Gw{|Td=cLwl z=5}KpQQ=2~3#NAV?4qn$?vZDS9)4%uD0#U6XYL&j+@}o8Ig2 zC`S7r_rpE#Z_{qRqmb`+8|mY`GZl+_(+(&R)cm}xgDJc1UuE(63&W?cgB6c(UGFbJ zw&Y{p&yl7@gkbLR!@~r#x;>wQ9(RO|1lxUk?{IZ5J`5YJ=xz_OW0(*Qtg@>L2cx1k1XkGyP z0HS@P-Qn)I1owB*lv$Hs4mTUk_ZVkg?{xNVaR7l{ zqw(*2JSPktqY(RrL*4hz4#{mAN;9}~_XBXARo}l{Q zc;@z4dtOs~w08O?xcc4CG&jbR^!l%Fc&|!k)`1ssE$H<}o(Fxs+O9d=?{jOFHdZ~; zJ@0$Z$?Wpac~|3}PdN6!QS0Ox9qKTdMu`slcOHA$?kMzLD;5GBo|~ydH`$i1Xk+s0 zp=M;!;XUcvwIsc1^e$_V=MuNGTXzN%C#u12-gy0VA5N&>7GXn@y_=oA7is3Lr!0A$ z7A+@n9T}O6R%@8fFDLBQj?ppvdv|xY58PjAMiZHco)~5yX8dvOzSbYRGDV*9sx4 zGt}tVbExWY)3L1XxUVVWirt(y(Aa~cIf7~O7yuXTaUdLy?icvP<`A@r_O=vftBXxk=)*8 zBZi*KQd^_LpKEQd5b(o(Tk)%F_I;yO&D`hW?K8x4Q#&)Rdqyz4^SOI@k3wUne4WS~ zK&b~*ux<|(y**UnmriJPNjA;!KVJ6wmx{43pdOm+d!Cfwp4*+i-$8}V$oCJN9q!Iy z9E&Eeru*D8PsnEIhrxTD=(XnA-f&E_1;U;?dqmxN+`Fvq_q(*5aN_I5dL?om!?CQ* zd~i=Oo9kzeh2@hu&GsIs7maS=J+Im4?q(o*JKo8*j(2Yab5v(0oXz7cV$SuWaSeLA zC?V=PL(ca&Rq;b4&f~n+WrhR6oIA&JH+|=7+udlbV)kkBb|Ur{JJG^sov8Ee@4PMb z?e~qE=*ijc82rvfW>!x@zBcb|yyv$2xEt5EZ*iE`Y8W0cpFQn7@^3kPhhA8O^ch?4 zdr&<`dk*>dtv@eiQ%GLvdV95dZ(=)((7$@3ir3y?mo9C5*4^G1uP=2tL`s9*j4k)P zeGkS&g{I8oLiS$qYo|xjzP`zUx$V6I_oL~nJ?Ira-Z;Fyt8OQVDZ=45ebarc`SMG= ziOrqv2zNUBF?#2K_RE{ORwWGQgWcD?-8%bL?JM2r=Ovzg!yGqF>mCfu_Qr$Tt|I-N|}GI$Y$Ica`AWs}lI;4As|Dvh3^c zbM0?;bVWiYa`vwyo4BTTqk27yJX)UEna8>57`S=+!@Bo3eQI5=w>!cqjc62axce`( zDC!>7&6vHBZX}-OdCp#OU}$QoT9@9ZVzsVUUESX=cIA7Wmgqxw2D9MsFz)d4x63C} z20e419bWHe60wCmrPauLINkf~^WSpp96JqdZ!_;ho0DE(<*lscxz}T0h@etNhI@NH zdGySk+cT{9Ud;q@n^Ehs;F_nY#qAqkKPlJQ?{h0Hy<<=vlD2EweS5>OMk@Ikq9=n`u6TxUBq91Q2lB%%_#IXa=oW_bYHgYvp7(ow>9%WV!-g%-4(pUa zkky)==NswMC$#k9cPD!=`h$n2Dm+5>v$8esVhS&bRr}N2u8IOmf z<#qUsKVI}Xnvpw>fz5x%sBMf{u?)~v0z=6Yu-@M%b)8}>&&Y29x8xQBD}$$)pmzT*LwJ-mEa@ym~m^VujwTqAb)XSQ_Fzh{wIz~6dIdk%dy^t3JZ z-phSGacdcx@@Kf_b6$%e%s}qjV(&Ejy`0Z*%SQLTuO43r#X9_Oc5Zn2a(+JE^1gFN zdvqm}9(yd=x858SUFErSneH3sW@u|Kd(XRap9wHIseCp*EUk$h18@Yo{5uQTc_rCfcUxrznJI8Z0?px&u@n=|(>BHi@H&3^H zQc>oetIrRqgI6Ora<#|Y@^0f)Y}}LG=a;=X(6AuyyUT?R zGQ7ubdo?;Y=aZ;YkBh7w^7LdZtmeL(ykEAl3frBzNZ&q*-g&R9syVsA}5 zBIl!2eD05XwHgTwCAmD<_9<&F4`xN3n^cPecpmh$lUdGlk)WBK<{u2h;AZf`cZ=zA z%aTmMbx(U)XE}C_z@^~=*>?Nq4~LNL>;$7y%Elee-yD}pYq}p~4t=EqL`8#%1<(%7ky+U42>I+TWn>N=Ij5XQ!MlCAUO6Q6!${>>)S7 z+3z$G#qPVn4O77SN)AEO?fctvJ?67%8`%4AyUl8qUlu*Yz*Udsz6E+fNo5p#9&A4J z>6@QT5%Toc_jh6AkEA&czSahObWP^Eh>sl%p7IlWG_!_R#Z&H>He=d}?-6wmVEQoo z272!H=X(|m^umF^A`a_Q!X5g* zzTiZgbO1gBzFYv&$|xEGsu_F$_~;3cB;ZePJBQ9Jj(zJnC6X8#)7$KHFNr-m9~J6- zE8j6I_It&&W)twedjJ7vZtd8I-A4)b1-TU;d?*wF_EBsf2qpvaP38IgK44ekGuNhcbmf=@CCe_4`9N(9}Gd9>_a z?$5sX9#5u*!da)4&Uu~e_pXls#NH}>;XSAj$A`p5XSrW<=D2(4tU=5huio|whlg;| zyI>$_@1EN?JdPXNKh!FPDdc`snvB43d$%I%6$l(!k`IT6Jo0aLdEUE|U7)+@c)MXg&AI2#(X^ zJowK=?{~HB-X4YKiXGB7DLg1Me5%`06FuE;xrR;8Cbnj7YPf8g^XszfW*6@6#iZRx zI}YS)-JbR64n89m44V@=GfLFn}dw{O}}_} zd*Q=&{a}$PCFP@7R;43rIlJ-F9fTjJ&^!U+b{_A<_y^b-Bzt4CG%!%~Hlhmy<8hIL zeYo4;H`xw-j^+#=nf8mG0C@{V@4Dpr03_kqM(A)de0}vp+yI|o_-ELM$az0{-?^;! zuZRxUH{eg&^>*|776dmX#em-)-n#;L7^w~BtIcvK=Fe}qo+O>l<88;8<6(2#ay`#= z&kZB8ZV!7G)G~@paNE2}#pAnAqwPd3_TMJ#BRDX-&S;V_o>!NI_qz{=h?$Uku+*#P zJ)^HIBy1x+k|*eRGq^hm`&tU^v^S3rY{ir++UE;lCPiECEq)%TfR`HPGwQOHx6S*( zk8Uu&Lw-BlI~L)&$a{C&$GzTo>b~dcgU7OnqTCT}xr4G$8@zA__uI?O{bz7yjoq8i zrI=Nr%8Zn+UiO^u+`4zoe0rmNL(V{)bR2o6_9vbXnm2dI`JKa39@h^@{TElRQB2!%b~hosHlu`#YYWd%ZHw)4X#aDjzx9Q&YV} z^Y3Uo;62&yWz0fu_?}y2m9KS-5b7^2xt_>Zy{bpKot`>bzDN`oz@LYQk5qdR=ihaQ zy*|>p=0ABRlAmnl>OG?Q1HL)z&E1%otZfinzI~Y+qi-%Y8=0*QC-1E|vpw1(_4j%? zj#ZxQ+WsCmJVZAbW4PhR%%70R3BsSSF7ZZhd5MA!)8P=*U~Ap)c=M9*!loPEc9k6U zjQe|`ed&p=S9!;x5agVlME2&X7d|}Aa803%ToBXeSKb{i6^p~p3>rJ1m8Omus!jFw znY~P#*)sSRC%o|zPjcE%v%&cdUO1Kw*s0Y0-fvjg$9sA;?=#Csd)YqY?)ET1qaE=t zx$!?}D-f~y$DZ~b7wz`w&8j~}qW2l@@%X&Ksr)Cya`(DHcm+0LK$37U!-R`{>aEdp z!r^H{QhZ-LHR1;{@24B0-!Aa)z4q?OS(-58fm6%9#)f|M&dric(d?DHbj9ZIaL$@H zyH8(im$Q8Q>yNZ}dtjz8;)uu}lh?d<)JJq%ia1SJfK+wYH;+M`x#1q!dN~#{cvw7p zEDh&-jqJ=E{_wlQ26vyQvJ0z+4OpEc+307Rs@JDII&${4#PoT5eZJpK&F)Lmx^qGG zf%{v}dUAoS--KJ+)0nZUWCTDeVy_qCh{*S({6(67B-4;*kDG-j%5 z$U8mYwl3>}=xfK9W+m+uJaQ|{%YoP75~ zJ>16X&hp@Cad<}Qp6)+ygR4dJ)nCxFp42nECvqEuX+u-qZ!bS>*A_F8{CrzSIq|=J zCwgWT>09?+wz1y@>r3~mIn5Y&xFLSYto0Qx-qphI&Odp2B(Q2IR=h`)_p#+p??=Zs zFNn+#Vc>{( z#3W19av9T*_q(uN!+L4lw(hc5iC*@L1I168dx5iiaa{4+gMsSyVD$F9%7fgiW(kYr zy$;|}avr@cVAms*osZMo;cm_YYwAx5}IDp5C2> zdzZYc+|o50huC9VqQdg6c$X^_<%5Q6*Kq7?Pdl5MdfG+dp5LdpcZ2XxI&PlGHx^Z* z+t`aIb2Y&?a4xA>y#O8CveXzdu?j`XLQnQ0Mdsh30ygi=V$Y;H6X!X=J zA)5AU`$i1qYdm&~()K8&Og_ykVw}C*v8UzfOYY7d;LzIU&c)u!owHA0c!U|f_U}g@ z3C*N37=4_1n|IvA!pGaRmE;A9m*fs9?e2thXV@;hyUQr%b9Q#)z0=#`b2j?E z&pZ#^g&$ym@>5V;xSND^3+|<6t`pe(gj~+MC(S{+S=Z|?bM3qC^v@fzXQsWU9`^_4 zDSW|VtjSTUDb5pb-t^^tTupee{gJHm_Qgj}UmbqSBwLQn^u^e%ay6Qwvimo;8wT>< zEWO&#G}1mkJj9su+2N0(Gt4YY5yh*Xm*eA5jy~@Sw?y8FBrmYx8t!;5ediarOdMYL zJ<(p^co)wCqc~=eitXTyo9}mYUOn$3k|V{v^z`lat@UhFP3M zcY^TgN30$f&GYQ+@n1hZ+A$p--#h2`c*Z@My~uYtlTh8|vae}*mx0N->w+I*7qRRf zxr>w_uZTY&?7rMt;i-E&*ELR_T8Cco?0l=tmy~>ECJeML?^qttb-(L`}zV~~h87=QusC$I$ixW|IzUg{)z4qpX;n&IodKIeSol@@X zLU|7X*#$Jkb)PGpgIxD9fDd1^zk%^EWtU-m_05V}s5v z)62bmeRsqubB{{x%{}UTlvjH#M@DWy+V@)YNA>%RZyVlSIogZ$;PPcCL6!GOX0B+N zh~Y`JB`SMix~1!{&Ftg6_+F&%bL+giXuZkCw}Ox(?{3aXTVUSt;qBbtb)X$$Qg%?_XXNbLrHT+$Qwtq>Aof zx*u=Dv)$|&+u@_R9rwM1o>z=}S8{Ss4kwRjNL{L8Yu;ncOxIfRoQo+a67wz@GW`jhj1GdUkF*Q!MlW>eb0Q;^);9S-w2@xyzJ0*wkhb@(4t>4r_qSVjGs3>z z%&BZvR)=ysFSz+~beJ$P+PfppTerH=rysA?hE~^@M;?%OmUYEFZ9E5~@2eTFx@HG+WX+GfF#F5A+06)7 z2hit4C)i@qU)X~e+lM{v+vn|189m;4D(y3evqyTiQSYnvFJjT-6;19!d_ei}E8>Y> z08e>f=fye0&v+o#k50YogS(+Y3C;Ty%U{0sYV{naZ@g(b%gmLn9D#nhh4;PT&wLlM z_b(aAc1G>*H#X>`$G!(|afIi*Mtg9DSEkMAk?m{DA8sf}Jsx+0k584}G&;O}%d{oE z(f5y?>Fj>-oZQ4OqUif?d&}7U=!$i$M9t+jlsBWBcsh77w0oCVbL{rtYx3W3w})Nt zZ?joD*h_>FlYLm8e&Y#;w`QR{i%@sH8>xr!+^dD8DY3Kjlfd_;a9!&BlF7^MCt=?9 z_?)}j;p*;{%esMHpK)7Xdp+&h!+XK{toA#Y#_il*Le%T<0H@gJ2FSY5H za*gNf?Dv?RgV=jtxpLP__IVk`MLW~DRgO*tgOK~QZlPNhkzL~T?uL#Bk;wO)R)X9Fsdrw?QuRXniEZtDvv~GJg z2C(i~#MQp!_(wahRwPHumemm76*ziwgsFA2%+BGf#SP^f>yR$Hyq^26Ob?|QVB6=m z-0w}que*$FwC))EJ&O>k4`vAHmEwWZr)DM_wr=L&$>|4*8}8GWjiF87ypCS?ml^3_ z$ycKqBh`0`>xXeU%6raHwyo>!Xihw4&t51Ych#)Gy=HrCfET&%wJdA3`jqkeJtVu! zw^(Uoqc;^G^@XhjI$yBoXBa&5*eAGQ*zV_wh7h3X!*oxo^iyb~CO;ny*4M7CH$A?? z(^za4s`rbP`s^%kYzx?Tr{*{+zbwJyn-lKZTyZa3?c{wA zKP;m2O>{D@%-;!*do8&8aBiY%-0|`6XsU7M6wx=)33m^)-p{*CVl(6IeplVNDHB+R zdW;%)IqRfTb7aQC)i86;%?MGw(BPedcb|Ev8J-x0EUHrPsuMl)qd$xhqrt^-Yd{_7JIBPwY!_tv{bz=mUm$u*VEoGOx;g1>;#U)HzgbHVT`HgB?#Sb$1zOZ@jfd3Bie2W+q5p+PRgWND_lJT^Keu{zKeP7qi;(0 z31iX{;s|rzD>fX98|*lx_h*NZovmxiyv6dKGIz;muA>JEXw<>?QnX|zU?&nE#F}~!TY%|hNrEi+;3{6``kU;Gc=nfGIho>%Vm-YKAl^#I}I7D+kW0?9&EtZb_{*Y zeXG*l$DO*LEDznnzgdepV-;gQ@C!_`FJ7;}jr$cI2P)n^!Bb9X-5xj3HPXiJuew|B z6R+t$vv=FaJIBIAGsC>|AgAoS_r0L;Tr~P%OXu{_m_|I>PX|U#y~74Q*vKCUoD-1B za!CI1r^`G&&)e(615sD%LGg}DnZbJd#M&Ckm%;^}65Tt?UvVXD3_TL+9?rnJrH!CH z4o8Z8xQ7esF$88AKFn0@>GmQfXy0y!1$lH)*z*SX`?GQnHnXv`ZhL$;u`gXVyzc{% zI^5n>gHhKtzh+}HZ0*sTtavt@!w2pkaD&n)lavo7 zHh4_xyV>y6t32s!$8&pBci#=41&CA1Pj7ZCltQd)hRy#@PVt&pNV8O`E1+ zV0<}f2R*b7qKw{N=`{Q4vp8pGnrvbLa?Z|QKL*#CdF}VJWY?aa&o649Jn5U)gY;x`a>-gdALwoOs!j2zz+vGCuoi5lnp9I$e z-sc=X)NdV{K4EBHz`t*TGwVQTx^hql|x*7>Qk?a|`a z1;fW?KD(;6U_&w;2|>8m-3(kc*Y9~zsCHBbmJOa*o-M==-aJRV?-B2ha+8S)4|j%< z@EScwmJBm&7ayG%c;CD{2grQ`=`;=0&^ApB`S;;Hq~$wrbGarf z*5dcyefQr$0Py~}6MS#EFFOc*%=bOpw8%-Y_-8|qgbzT0)4eA-u+w2p z&<;S$6Pzbb&QGc1Y!K1lHc)#`1p?m3-lkBZzX5l69s3Z#d*4Uqp68`Aub&8-eO1-=17J@mFkgGx`ZsoV=Aeg8@B_m}cU!Cv z3?DY%48fT3tk1e5OArq3-TQZRb~&a*``ibIp4H$y1Nw{>3`@{E^lkg^VkKba)^P#Y zxy|JdGn&312XJkFNt!6*-a{?S#sWj2&$rMc&?G%enVH?A&H=;Jkv+IlJfy}vuL2HS zWP9o!=iB$rdpBf<)83m53@jI(AD)tSXWx3M3p4V&if<{2MVZN5=ZQ7-`{VH6Vb_7Y z_)ndL?D4?i4|IopfPHv!e0J5eN!j)Q2S5bngAfT8qnY+fKm>Ddc6sCPzVP;ay?b)p z-I}u=4GYz|?8x&%sd)hU1>!^Bya%0`+Zdhm@nLqVGe}&H-ecF>dkI46@V@rnd+vXH z0DV#P(i5KgQ0~v4K9lbKIek0#Jx{F9OuWhx04?ue_NakGJ-z{7(QQ&*K?Umtg_A-MD!pM9VV-k=mPZ*2DHSKNahuiVQ9{f{l( z+PYmGW6TqWu({ZhSG%{4>Gx(oVA;pf?gF=rwYmo#Udxic&0g?qGlDc?fgRC#t;_)W zd&ho_n2B!P#s#)_0-zIA|u<9?0IHY?$mS7yYE4Xx+3?P%{>lWDd)BC zAl~pwCXdwj|VRQBY2`JUip32;K6X5vqcohj5SI?gQZe%_vgiY@4Kf!;d< zA@6So<43)>9Ts`fDCoR2dp(LR;Gwx!X(mJLi*jb>^wr*{7mnk->AVQ=R9@+U-5&IJ z*tgG?Op7%G^oVfLbYH!sdkurxMP{5r?Cr)l=M7_W$2)i3cLy2l!#yh=@O$zWgZ7U- zhFVfneS_Ae$6`M2UDbf)fseQ~@e~#J)~@dE?}ygnBbJ_?5-B!?t7-36x%hkG!@w4A zvK)YqA3O=wzKuhhx7>yInb5TylUCydbE1xnzGr>sp6lD*(SRN2_L`@jd^~UhKHZEQIOgW|Vag;J-S?NXcMrXpfa^T3Ze$}k zHvG4@#SYv$eJ78SUbk^&&RI7+eL0?B$|yoVi*o~iHz($;a@k7WcG=cj($_D!AD2z z2ye3r8;b2}4Gs0LN2czzpJc|6<0(H&XFrgaFqh};$w1uKwa~fBlxSbK%awZ_^5({j z_qEtMiJ^5F*D5O|Bu(tFa^^R%>9f7>MLgH6%~`%CUk*A+Ys=o7wckOR?7hAt&{1zVp_5$EC%oKYO$r zxnFx`CAT|HhL61Yb=Ye3>k`B4+>9fgr`z2V?8kx!yV>G3A4Plm_PdY;6Z47q)FdCo z0RI#edM{;!r5Ol2(C`PM?Svwr7L=e}F4;l|jhaC?Z=ZMHd%4p)%zvOEWM9xjKRy3D zP>rge?+rg+mIO%%f@#vtV#cjjbX7rOt+g>xRc(q5wYu;*RW(I;0RFHa;UZEcDM}~t z1z!WZwcjTjMyNg;6g5FLM%pf*0r!H)5TqfWr#2V!%;yWX&RPX`C{dSZ>!&+o&c`+t ziK1jEyHv)rP<_j07*1pIUDDRW);e(?a6(!#(d+T&su7HoCe_6L0 zk}pq+kJGy~O>%tHN9CwfArZst79z32H~7#+T5O>Nk&Mgsvt8KPDvVxRu|*&m9>-19 zV}ud;-0GU!(7H*&+af5$LoE1;SrE0SiANyhaFPoJi}KOyy~eYOq6+eE+oTYpYCsEG zdt!prVn+H{>!e&HhNL@hyE@9t9$jA#6IhtRV>Xqb0@c@L4NfvSlN_frXohi12Wj)F6`NGg4(d|7-g%&IO@eDilG#W=2;7p zHI7ens+xvKh!=YHiS$GPBZLHy2#|#JoQa!q&LM`=n_XplnXL`kk~D{iW&k&uI8sH#$lf5jQnTxO5ri6> zh+)s4G}kL}r%Q1LNEVhP6^mm+Zo%l}yvnI|XBIK8BHNP9(d&8X2Ns1cAWp+aZPnFP zWF#Wv+v7vJC(f@~S_rjbU+Ja)+lU|almx*5K4JdORCvO?fe|w*Zx7<&f<0wZ*aPF= z3)A9{s1zbW@$Yy7b%f}53;2r40oGyic|3NSNF4wWf!N3$<$(#H5d?r85%h?9Kz*RA z`G2?e|F?(a{2dOP^L}3Es>qfnjl7|$BVZR#s$tIaoesj*2?d_;&n+mMyciiP(zLK(VA!H${ z-J+LW7e!Sqi9=Icwf(bE4I>=L_h@U^&Vkyr&OJwGI1)}mQ>MT!T4klNg3H=XyTn z$mg|HQX`AEH&(%-Z>86!(h-d{Z651Pnwd7%s|j5?9XY=C+bh)d?nhB0Ci>7-8R~%K2`al>Du6D@$Tl6T=<#aytg3PgSkY6E7x# zGV(5P5{7_o;&~d&rRBAuC&-VaTZN4WC27n97mCH>yv^h$)|htZ6?Z!_P2<`(=7zEJ zULb&)&BJ@ttsd?ki(ZOXw!du`0BdjF-A~fQw)=q**;qK;+j5YcyK1)y8@gURV3D~X z*mmta)V_G4*fgfD6S_6LtF76C)INR9Xjk0X$O+W5ByTC!im4OGOMx~PR-@_G@xbSH z(2~e2LFmDgp;ekmV&~1eW@>ojo{8|Pifkd#nk$-3(Z70oYu@RcLRsP11_DTwRuL|+ zsjv2SHc*=@L+gY;W2AwX>gCyk})Hiwa!fStg;DZ4fr zrMYr8W>=0d8G9RPs1Qk#Opo1wlb2J{oL%>x*p6UBuViP6dEy?;ChWjc)Ol;KVqzxr z&AX0x())$$SEi^MC!`2!5zD+UUm9TS-K1u4lH=a#!QND&QT!$PUAIAWq7oz+qXuI# zHd|E-y|bfxHEFeNPYGcVrbuK#J$kM2n)4ty(TJ2VSEyqYx31_5am+FaUWW`DN22mk zLDn1Qxy5_Zs_pf%@}CvyS!scUAfqm>2pXrT?C9`xUbQ0er>D}u(Z$c7nfJtn)l@Mhbabc>N@n=2zdwFHVY=oYgG|sd;WNJ4FVo(~5&ugM@eW}pi8yE*B?)8eK zw4Q!8>acg-*)~mGUM`Pao^#hkc$Y-5(K@7*#J0033t3?giKDi&NJd9Vy%z;sNH`vo z!gc0n57r{(b8C(-RlT-R3)1>G46Dud7#VhFJ=V^B2<_LfeLdC+!qt4;#J3`Mwoe+O zb?djYcck#_zMJ7hU?Uobcn7jv_`{iT!3cCilOV+c_|aUo>cObfb{@6lmU-o3&ghrx z%KNy=6I4|j-O(^TZ|D68f2x1VWn5vP8gTPd8ck5^UVHCedQ66j61gC4&Wh zcI$115P+@`fQtoIt#9O$`2D|s=Kls={$~F?&Rm<y zBE8fmt4BgECKw*!2e zgHNOzOj=xulIIS6B)D`>o}f(9Ts9{*RuSbD_3h%(O@#huA^Eoh(9Ga)S5W@Jc_IrgwTKDF4p6H{(mvY^>Q0vm=Z zi(}JwSq%XzfTJyCkV!9?HALQ9#oF8tuYwQF+0vg$VItx({5F)5+X>dYsE*{=K%3GSfX5p1 zXlGl6-Hw1stRtg7Wrd2YJ+#jtuyM5gUPVrdBq7!&MHcUlbvT-fE5!@d&KY<&_uk?r zc%Kec@>u=1My#-#E_LZHa{9)4dk<+6B-V|uHi4w|w-Z4TWWxn)04YzxnXbnSvA67u zgo}h*ZHg|UMTF4!-hSQN>EaVoI3X|%Sk}0*m8G|9Jjm*YN_V>VFvl(KIhzpFsTrSS zA#PO3AXGCnynS;!!AY3XXJnE}2&9AslNLE0xtiM>UUwi!ZmrR*#K;(yoSAXsbO0%G z;79_%h6NL6pR2MgW-7j^Axc;uZKPN^q$Wot7CM9p2is%(YgogI0 zk}wca9-)3|;t*fikR0J*jY=WMxrE-%)`hW^AdufjV&_A(j@;+C0Yvfv@rqKFh*FA1 zilK^vil8c}DFPv-qLP|esaAl95~&JerD#ZsMv$V1$ORML0R0b{e76uf3i`3^1b`Ay zl4Jz#MIK0lN+`b|N>JheeD;;`o(TL!_mxA?Am~9UMOWM*LinD;>+uh;d_?*J%BQdc z=^!CVKp+RBf}K&~0G)wBDFq5d4k~%)z3-qN!F>pc?iB!`QiM_9qxpb8L*$em{ttk^ z6nm5u@Q5Obh9ZN%I2e*<4VOhF6f~7dH8#eIX(^gPhzKMpr6LHKiHL}lm@1Hn zih`&JilTyMqADUHpoM~>2!=|EB&C94N@^sfijsn4rXpZyqLQenf~gmx3Gn-uDEblV z`hdM^29ctaXi8}qfTUUyoni_ErJ!13R*|8kp`}8FC<+<{m?&bPT4+Lqp`fJ_hJulZ zh>0i~5{g2KMyL{$X#nU4N#+mO*r};p_+452OJ1Akqq@A_SoT zhy=nOp6HkE$WF-)fdKOYAV32OAcO<-_C{Z4N=1)XIOz^B7K0M{u3E7ZE;|6 zkdSuU@^Fos4Vzxa610o-UlVRS0(pdMdZ45)1zxXalpNp(k&CO^8s$z+kiL7cW#6J1 z1wUrD^kD679T!rzCziJk=Gdfd1mZ24st;@IdZYxstIO9r(Kqi^yk4ki^mkOzdM8nu z)IEB*ZH(ro5beUvZ?9}MRrTTad*4H-jckI{cdu?dUDm6*-s@nXLjo%_>8Fft_!Pi*?U%O){~iH0Rm zNv3gpaBMSP(Yf)Es;*{}y3RbNyAj)5Ic_ILlyWnCE?$ncz3L*<(M4Z!FL?(m5o?>? zw`x%8Pe?A~Jy8?8EM=hgU5B498oL~Il6q8F+-*mSJ(DXk`?edooKqnv8&eWdTBbBX zaI9}uK5l6Er*(p5BBO;mtz_!(1OYcQSVW1n3ar_i7-W}sHV|n6YJ|nRB5-`uK-sN| zBc}&m@Of;v&#B5NZ(}VsuM2d0oWVn@G{nq8V(=I`m{}aMSrG;@i|2BV30kch zqy+se4SO~xp~}dUO&+t_^#W~D+XCFha1yVxW%YV)oxU}0RGA4DwQXDS= zI@Ang(=lzvo!0UrxQVi(m6@$0&GK!dq)IzBtv3N(^o41lm{D& zJ0h<)ZR1yqo!(o{K;o*<_G!AI!(2aXv7p+cF-(^cR>4%`)vos!(RtMadty5yz+@JR zYf`YQoaycLaLCN+L!(*_FDI{0Ywnv-n;r#Y^J+WQZ!rs(D9-Kbb-=dKOztVB>Uksd z_Y zyK-S~V>L=uxp4;z#1V?TqbZjzY_nC;o(i_RAR;WdqI*eURq+C8%qH(hYV&hQcx!jw z_Mrw5o7`N}U?y&zl2Xw+uF<#=i|dDJuwDH`T3YHSQwtZr;L;(78i3&iHz4l$@A zw;g5OxptZC;FhM*=UG^mrMOThE(L^I@l=E=xHo=!HA(Hx-7fXE$IXjkjSaTzU6)y? zTq-cm7`C_>3TE#2d$i`3oQgCy#$enhr;bsRuQSSew(Q&G=hLC%6NbNA7|CQ)+Y!pn z-R&E$9?wRA7S^LhAT9L9jyzo3?7cN6;G*Aq-d*8NB`3MaFDjmn^nP$ddF;VCu~B6L z@wMoY-N4r7iXQ6?!L2>F#zv&Q<9g`_B~C%UkU)j&rjvTu{nJxz(@yuB4#J|1Eixp- z49AqYOgz_!U1oT*#xffp5cBQZn|W$21k4vOGLwofYkA$W^3a<0wAkdk}JnhVKG;0yeHm0`KcBZzM8m(hDWIXucUJg+}tv$O{me6&Zu*#Aru&bT! zlxvEG7;P>VmX+#JS2Y(VToyRt1znHMN3W~PYf%J=hG1hObc((dyATrY+KktG&(|QW z1|F0kyO?bS((D#wK7%EsUrFKx>$?Ua6%`d0AOZYjgg>|T{lCBO{M-EYcUN|w z-MPMbz4tqhM?IVpamZ#vFJAI4-+5Gf+Y}dSA_d+$@Y2Y}-=!xVcVcLyNpmB|_LZ?k zuuVF3o_F3ltG3N=95(uKHNC0JdfUja407Bso!D|YB#^D9GxI0qR3YnY)RFqjeZFsJ zTDEKhNuab{;&qG0_K$4uXqn5Ar~`N z+;`dYCk4(@f{G}1(xfU&KTWx>S#Hv4YlJZ+GDUGRewP7JQb(d@ytV9=T}E8+#k|(q z)KOO5ILrPWXA9gEhG!YO3eP)-6Db=za$4i2B8Vkguof|Rl9*J6SBgYMswr9Lr}Jk% z0r{#f8nvBj*LG<>db(@vVX`!6a*HixZFR5|Qh`ak{4W6c7#f;}o8#^9zWMD>21G<8 zNWu*78v!j=5BWFT&(FU1<7r6z<6}l@pH*|4Z6B{ds)mjp(*1c5Unu^!>ZH>sUo`Cr zAXq2UThSmdGpf-VC;Ugl%ahiD;z5&P2nnC^9>({M79OqEKc7aeQ6HfqRPwn)S+w{vyoePs%m zpAn4-1ntl_q9;Hhi2^}@hLPIr6Y;0Hg?N1k4UBENMya7h;#=PneLAMLVh~BYR74_r zu2QZQwT-mYbS*btT~i=Jazu(Ngkcb2TxjCa5?T>}6PZmg(I69_drZyDH87^T$=KRh ziz-p*w#d3v zW-*AxJ!6Z75fT^BpoZ!<1YRY8KQ3`-Vj<_|Xn zOcV*Fg7yTdjv7ssGN!3%Z&=ySd3RkpYEGt?~4#26)2`P18$^^%s@Sqo`U zXUFT0I8mEPn$k%%kbyMj4d*)XPIACeQJhZVPB4KmGY6Lq*j0kTF{EAyZdq$%Ef*^8 zO$IDnxXaG1xz-_>SdbwI5wT^4?7ddtMB3ejX1vvEo{kUiDJsqwpm|ti^w{3SEZEju zbTBl|j*wq%^0@13o991rP|X2d9uBK4cBrC$ir&54?QY$=_1(7Z*PQque7;{kDEv`8 zm&OJA(t>C~K=yvWhqVu2o*<9p3kZ9G`3XTwLeou9G{r(v($f<{6p2M3lu#4_6A&dS zNK#Og6-!DKp%9fw=}iJs($KLn4It4#P|#3JR7k$hf%D=I#18=n)KFAO?$uQ-K^09k zO(g`uNk~!^5>Z1zR8Z#SFz1&{0)XG*wbXR0S0k zDNQ9r1q}@iD@sJsMFA4E5lsZhP!TO6s4P_?B9R#(q=&ud2@gp<&Q{6A>Yj4LxH4+hztj4DWoVClxaqq5`u=BC`t+_0+6I>LWY8fh?0RQB0_?x zK%@#lqzVRv3KoiqN>HT=N+}8ie!qHSAbAVxRQP>LzP~%~eShO2NT)MD)* zP&*+xF*ugmX2lmtv(}r(mB_@T?TQ+YdC^rNUZr&1#K{`2{icZ&!MUO{fmf(vgKT#k|q#QdDtOY9nj;U{K3L)PmWB$k`xnOc;rFmUHI8bYfRuKWvpX7%d-wp=Nfc za?^+6pxrqkb1?@3I5SeQv0iS4i9A`S4f_q3dM%7?XkPmI^0$qc7UI~0nDb7lfRZ*y zfl$qtcXH|qK~HUucH_}53&fT(ip}%hUD}+z63X$4k(!?CqinG;24h8hh9H8iM>cv} z(d((L-d42UNs!p)s!fT!s;JZx9KQDVo3Bo{udW95s08bjR=(YlOV`^+Y8N&z??-2% zrqpRW$bBNax=Q5WTA|;KO9iQ?P21{oTdQYO%)T$Za~-1P&(#)|>}Za(y^E8rRMy8my0*6o=iRZV zu}7l`>$84?cJe=WiDJJ#`dLGF+yVZKRvf$5@JN@qDAQ?-d;)#SKgbReK`wRkD_&Wapt_} z)+~ZsR>U;@uE?;Iimey9*VDpu^puH|XVcPi-O?&`PC#5C8`RgMN2keW{bAtLCLQBxp< z_A0%#&swIY^UJRKZE~hrjE4c+bWok$Nmy*xa|l?*v?e=FZ=Z8J?YYnZJRl4dGS6@+ znkdaqlgl>rRU+9FM>0QL=F_P60#tR zy5{cY1YD3*GdFgi+L|;rMkDmacT@*8NucQ=K+9_hGE8<=)a%{nRc-Fpot-Pxs!&h_ z*%Ws7z1Q5IPYMWH-FI_kz$%)-`A30km`Bgruk;3FSTNow99Rza85< z$YWxNbXmq@E`8cxN1@@uN_orPyu00*S4B+{ny$51dU9%kns=}&SD%FPwc-+UaidG@?!5_t}Jd#F`9 zDCb#hn=`bkXsJkSoYq;YYX-{E+v{JUJ>K&$9YNzb?(YuBo5Qy^FTjAZQIUr*Nc8N@ z7+J<7`CyY!cvh8lyA`^DDf}aAW{TsaRN5r69D86V?ad+TV$4enFeHYnwcGU;>svKo zhX$FLNXQUFHyNypuPg<+_abaY=3y5@A{2JvV5v7$he%E16A@7e=I+2KKr@j=ikwO` z5*Gb)t?W*+73iJMS$QgcKaZRn7bvK23Fu59izyxq8iGr~>|MEb8p)Fs{J z(Qi33|7Q9TX)1&a`yG@6ZfFCX<<{XJn7&$Pd#O(wqw9`L>$3_ym)AtWN0-A zj~g431>w_ndP%5H+{0Rxytdja*52<*QY|Uu3zgH+Z@o1(&|Ws_wC3R>CCZ61J1Lf! zRU|>Q;+GAMpjmrE(g<+_h{bAn$d$GVp$ejQ(T5NqkXF43TpG7d(llm?Ay%61B1InF zkdp7cR+ahO^4{xjNoj1laYkU>SxkGdyp4Rmr&yR|8qkwYT1Bia+i>P)oM&qq!Fbk{ zq*p6Do13fcss>#Zqj23>0@mZSU>H`$VXHGxjAGk@f-~tf;Iu@l$%78!rLD5l)T5iH zuS@3dl99U*DBrhnnqP0BN9w%oMP7}sbDzqn2jK0|p^yJXT={MW#Wb|Z+)s3Slu7$ts(Wgn`LaxpC#2Qw>eJnn@p8VY`(1ul(VgHY4_^AQIJ~bt zfzBi`#@D12eZ5td5fBAtRv@udQQ1!E$u8!Zp(Cj(=T(v<8I)vrH7FsjO3_Z~9Ou0+ z9Gp&0lE}Ens*ZTEI%czdBKI3#P}=f82z|OQKXQf~?x9x7G;Yp%=uoU8O`S6@XcdNb zU^koB2J-BTSa%Xx3auSPfnmx~BoVfSuV!nfo$C3e?=uH^IA%*G$x4Bdq3R*p+Q^3% zx~jM%5WM|pZ*+^A^jqmio-cZJV%?4kx+MkV#fz$D0`Wt-R0<8|tyjd=>KUkTXV2 z;b=veOGG76A{bsd!e4W7Jj(l-qPftZRuL?jJH3Wez2_Kr-rq=ejfg9??{t7kJ7{S1 z%JkCEigk*V;KSHu&poE$VS>E%rQG)53=k47Hi`mPEr$@i+N%t^K)Er2>0znIWLI10 zn#xZtJ6B;vsq`u8x=uIu$;hr4w3o7Y;Yj7K05tnP^N^pn}b0wrN1i9CcH z%+^H&m8r7B6dbiNlB{ApDA{aO8sJRY=%$JDSJ$hLgV$)lI2(=+TQk&sA!8P}h0)7S zuK-&>q`$r?fdMa`yZ4*D&~Uw;PWT_LN+y+rg!+f;`u&jbx&4d(9N+jlbLY=?hKcTV z_^OE7qAiw+&U8-Iu|XE0qit_~wk?JbJldpSBl){wvLqo0Bv^|=Y-|A^+drfHf2ID3 z{&agJng{yTv})-(>NSE9q9lZB6j%M7nui=BGoNXgrdq_8DT5qO9Jq3)Z`hj@RU6H2 zpR&W5mIaa>-Q3*5Q`EvJt=1Zro(Ge)crNK?Pe`w{JIU_IC7Z(7Xt{0#vXqv)QaVoV z)(DXf<9kl$A{r-df zXUk(7QMNVfM16uVjHClB0SW!O?=olanT}eE{3R6Cq2{Itr3zEO%!Ii;fh6Xu1mnTV zbAcmsA~Hkv$;O2hX+KGbNFzgGt$IFTIsYH-^!W7;UK4QV5CaM@IGWPNtckl^6GeoP zNC9Jtn0+LY(Yk$zhP!B#cqx`*qRQWCT!uv2Y=aP$^2-*{D?ou5VH>U3Y>h{3mEugs z|2P>YwGj5h-TCzLeDZmEJ3ElKcFB(bLZad?;UFWVRq3M|{(f*=o`yfgqWkhCsR4Oh zpxMr08sT+BaZ!r5`Lmk}HrE84tbY1-o&^M)c+i$>NKmQ;PAD3wEIsV;ug=Cm4j#E% z4DTu1od{b!L9sw~Kms<}PnS&z0+L*)oZK_<2u|9h>O~7%Bm->Xu`EdpslFz!5fP(d zBkOXJB_cPrc8!{yD(IjH6nY&KxhsHUI%H$IqS`7YAhs?z-4gXpRYsdlI&&FVV**Do zyRe010dTysk1y6=zUbf1;)eo|Ay_tw38uYzyycUB`Rm%Z+V|| zCNNS0c8z}KIbiSK961iZHAb?#rc~J9eg78dh1Ob8sMGg9VG%Z6)3}V@ShE(yZN%=EGwY+2^I}=Ov9Y1gQlp z0=pgtMY~j%cFPvUuT#hwS{zU`WKzlOBrpj`5rgK~_3GE=bs5v*HdvcJEhoAAu!I`| zQBqa%nu|!Sfl!LT!_1CY&x(3FcH&NEFy=Z*#Nsh0a2iCx7nT`Fuzyz51_?FyrI$O0 zJ8tA54k7_LkDFrygsa!iw~g)$q|OrPi3X3P45ZDv-5VJKWsSf&YcIKvq88^JbO^Ml=05eyc&5Yu+#ho0e+ zJ%dlfwDo>YtpObR&ZGwQqPZJYl2O{b5}IVxfk@e!Nr6c#y4J>3gBUfqJL|e8U36+H zB?W9mPaTQKE!9VHhVf-V_zI`i5Ebxxy?6kKaX!P7 zBnScj8hUB65aSc;?yX`-4cCWNFSN~EHwDw-;mmSm-%C@O(PAc*&; zuXq!T2?0VuVF6$i3Q`E1L!N{l0HXWmKt=bkg1w+nexdatvWKKV_9Arp{co?t|G=O2 z|H96WO&;A>{~Fsi53W_JV?tH;UDI=3*|KFvF&Btjn;wyn8E{ilq0AN;tVR|H9*o!) z1ml|iI&vIF%)~Wlq#vHf)G6Ic!W1pa@}1waxh_LY2zWv*U*AMd@8Nnm}s9-dpWWQ76Uik5+Fk^0%D3NOy%iyshbU zq`uun-A_x;x5#RA&vUe5_SMZrHX+Scv|+L4R7}3~ZNMUeGCQHPTI3%Z5w+Y6pKdme zov=i~Hd0#{_h!47Q(BC*>eOsoc&C9sO^Xt_=k3C$v0|AhaTJ1S)pwO;yKp2NAO~s5oniMxO9;aaL=*vRvD2R`QA}jH$=GIJSF|iEfi0iJZG^ zdD%Oie9~}g+M-*S=GfziMQE|kVv8QBQia>&y}h27S8`gHv&fkeGR^9>Bez+T5N9`} znqX3}Y+H3O@~t5NMVdu2BcX-#%Ho!+rirNJn>S!0p@t00^v9z}%{Ak(Xp2Z?5vJkR zHOkP~%U>jhcX`cgPi-o>x0bGoM~1CRV}W$43ZYKPdU9Y@uWE!{RP(+y@1Wa-ZSuZt zI@hd__6qZl*Hh^)FD)a-sg2sMlbe$3Yh<{Hkna7pT3b{iK};)yOd{kqJd%0AbJm?`5#ujl{E0PFV~TnW`4_ufe%m@@8`Ec+zPur} zsI#xB(kUDlYdY^+qwFT!-R<@1U!(6}Gkwnvy~$teRBu?crHYN<2vcOJ6>QR>_3dTP8*zKZ+N&E-n>Zi<2f z*d)OY*4QqWkR8mG6#&{a6z*=~N@RN?!U1qsB~g|g`U7J4$PhFj^GjK0x@tAwImkrk zSC2&EY3z8~x!kzVCXXeQ#PMsXxzZ}NO@l;;;;4=z$P>zWSA&xI`>Z|H9@it2LZ?Qh z*SA24t0N+ab6nnUw#>c8y4~-38|uuRs?)dEyCgQM&eoy6;;egWw-uR_ZW~(%0gHY^ zcL9_nLQ4+KlG4G)l|mDk9U+2*6i=VdF*7izW87()6s}amjyB3!@`qKfeSwE{Y-c(X zh}yNn?%TJ6lyN>5`*(=NqS`VQcS741noi`^lONR!SaTE%OPh$}%~?%4wM@q@O!#dR z1e>((Eo$J@ywLX2`;ENz-+S8nDX-3iH?uZol{Sm0<=ptw2;D4^tT(C_4xP>2?Vd9^ z*Eii0E1tA6NidT#0Z^D#WXwsJmge(>-*>lt=SOyWc8XUWP|uPetk_nDMYR_)ZcS7c z5SQx{QZ=z5vWsMTUNe&}C!1b6lww;+8Bh(4o03Jck8!C=?=R$7QNX; zdh%s@%DITip~SZY!G~~*W(hSyp}^W#38qmnO|=)A=}N52GaiBrxE@gzI>ML`GDU>c z5+F6I?~!@$gWX5v>3dA@V()scSn&*QM-jQ3v743D^3|0(QW*^zQWn`2KUZjW>CCQ&rwg*?q)hGa z1yj!omH1k1HNb)wGS7feLGjEDpw6J~_)t^_CZD#wt{pmA|QF?f?#93}pyG5}K`r|)J ziP4zg))^SG99fry)oS~!=d+`eAt~=G-hTCSV)nmp9(;9^3C=rMoRg;sQa+rQl~~9W zdlS{IYqOVLT`k;K&0?$1GU>}O+DsNYQ-GOH>aoIJ>H5H!AVLqdI=s*;=aFmobX{kn z*&;|5L?cofN1Qwoc87DX+V|e>Hmc^M67Ly^D?Tb5#_HKcI+8d^8X6l=K<3v|-SXJ@ zt#nza%(;U1xTs4|{d3-WwQ$Cwc9an-fn%=Wr=wC>E4LZfub`n0Zs_(Glslf@X7~z< z6QUd{imb)GO;)w4$mo$_8mE>iJBZSP(MmpBeMsMeceJ|QYhk{<;M(wvI?9_H7!n(1 zP_%6zi=zThCQZh1JAq1;>|lwAFz&~P1VJ%_6fT##lO}_ZUClc58?NY2aR#H?rz6W` z6jWU_nrF=+vsUWG0zYgU5JPe!8q@Y}?U!X}iqg>5e9D1?YDxnm!9g!8FqJO3OsP|Z zi)+Yec=K##?{`#beZ?lCwTo0;+p(*#syu{g?lq}Bym~)1ugE=Zyi4fNF6ZmLumK9| z+pl3CjfQ*G=S2DY%%1UFF?n-gG`ba#Zc5hBOcudYa$C}D zoUA!stzEm~`5OC<%`4+`!In@@SZ%|bhlb2ob#dh)LaTeE6f^Lf8fSFKp z0{HOG7ORZ>1S~sU6PZbCfHngaDZ;amX-)|_D1)@3uGg-Dy zy{o$uisHp{H(r|=wXY37OS{?Vp4AnYp(XrsYqMhCh=FPj3RuE^k~wYBv82UZy~@*& z9?~YOzU<}NyNs9G_2JKCraOJCRa_KLgt_|rhtcx1+v%gbklw`~R>;%iWSzA;M&&#x zDC?o4G;P7L=wk%J$#~H*Y78hlG>D$W@@uxZmqb_)5BY>@l&>Gq;z?;o2nqSPP{K2tI25h z%<{^3_4X>e5?j;O?c}Jj`kbxBs^nEsNPD)Y?bsVywA5#EX5T%oJgs#F6<+D5PE zhNENGe0eeO#M=aGFn7nMcZ%S9#aDYaM;z53qhfw_s_Rp1yp6u^Y7Wx7IPJUM@+M98 zL3feO3bPMSR5>?Z!HLyGxR8%oYG`*JvTBS_)fpthhdkHusa6d)iaYI~?A$lIY@ zRN-c%VHo2OiVO`PB0^AsrYCnXXxDdeZri(78KNztS+VYyE{lHMz?qijn8rMM5gIV$ zP+3WqEpF;iu?wtPtGcciAJ;--=QAtaG=1^hdtu!dw-Yf3O8w=bqg=F0)y-4e?_%Q@ ztG>ob$4!E#gmY;0o#S_X0zR_)4?VJYe(201lhCRA-gmpYIGnij2TF&xW@_fzWjjHQ z;Q{J%xMY}}z9rVSjKm80r=}Z8uVm-X%5zhBRo>Y=3gX0WXP>C5eDy}#-EFZ`-QE)N z`rjVS-y07kFfowjwBIrCnb;uktyR%qdLnwWvZb~jO4*X$!NY1$Rb0f3GcX$CcB`pq zMvA4|p<9lQOWZX$Z?7ELCs3R3dy+zLYSi#9XF&Hco9A=1!UXACs3L5p?q*@0-M!~M z-&#@u4+#h(B|=oJh1pvxaK#KAlESEKu5+-?8_Z83C1Kmgr#;_EP}jV$4y5JH&h`{r z+#X(ADsgIxT6x{!RXEK?W+V!D-Wz;#dBdgC*45_>&twf?klO%E0Wc6?pFQ)-sIdwS z69mXKc_F$+7&{tGDWD=SwcJ?+nO`nF=+(CSq}KP+_@3)M?{v>}82LuAPfLC0I=vSp z7+*OjiutH*m&A-Gw6EK#dnF`EB4no{31W~af>t3-Xd@v>DCLy4?7Jk_w!eCI8|-r| z$Jo(aTbeiP%LFBvH*!-n*<5zQm`+Bqz{vP!1ppTj7wa@w@>XyWUhFE1@FZf#@N_Sz ziz$bY8+xj+olWm^nsnFZ^Te;c=I?B*ee4GV*Km^#8l>N`)=kuEPyxZIS)pgr~+Y}ADTuXzjL1TIxy!f|qY}RBOl#Si4 z32wD=;G;@dnZqgt2=*2TDc&@D;uUUlvuvB4z1!(U$(MbON}8$9I`@55o07NmZ!Dy zbD$AI#~W(qqfT~Y=eRq0sl#Sf?=k$Jzw)1(lPhg061L+AtdvZF1u=*OktMblB}G;- zKxv?&fSX6;sD5CbnJ@qV8FMByRUiu$RSYb7%WG{`aa3_+m(5XvMn!;75Fsxo?x=r& zA#%w7KN@EYWah0s)SRZ=aY1W}dE-c2VSnOKPP$YcLxTq`jnPMyAOTgor||l3FfwVG zK3!aZv69=8pNG|e9vJjcj7LGn>G8S0a#*RDt7$92S$0{nECM4d3VqU_FPw?a-1&19 zDa_1C8~|Hwdm(9qHDwqD5`Jry#*$Fj=_ScTxWpNL>h$i(*vFS%RT269#S}!4dB2>) zF2$<4+W3ELH>RS!*96^@F}R#HmL9(syc{`pkQlK(HdwJ+Vx^C_K!1OG?}omz)MPg@7$q`r0is`T8cYbc9&7lRTTH%1Q z1aUA%a|&E7E;FgDS?j5V@e!M1lfMW|WjznZ(TRxx3jXCf@8;=36Nq#bDm1_F9?}q!d_WRkFyrl5wa= zQ|H7n9($2g*ATdD%4x8u5s+&>ES1Jg}!>KAWS5^hBu5zFc!w6 zf{R5u!+6#(j1)Q|)g?)tI%w8yMWj}P9H_8|64CidyMkBZ8%bBhwX@;CjdfrXMnV-K zn_R4X*x1yd?Z*H*0|6Nb6QPVYRGl8yMAbq7%3Bpau5zar@3vRyOoTEd76+5gEq*So z*6fdnf?X3;u-KD|qXDW(u=9(DuQt{>opx!p;+P_gDT8?g3P+So^W<&AWkv1%izLKn z38EP2D#j3;ushYS=Ovzd+d8jy_h;S)96oZ8#>#A&D?$M%z}{@GQRh*Fgsx7_SqZx> zCyg-6r5sC4?%{Oll};g9!Ym-5qzhx23s%o<)p9$XX_SoHCMHan&}Cy0-QAcAp>Re> zOG(ux?nJ1GDWbI)#8Ei{$03yfF+)&#-<<{gfCe8`1k_bE6422}Kvfh~1WhzmBS1t^MF~n$MM+IG#Z<8r zw2>3>J(a|H1qu|1(1jqBDE!g-M~V*>JxHHVh+eP2V9!=@f{9t#TA=!F!JCpQhUrPi16Vsj5B6=(kPQ z@q%`23sk>3c0wV*vWJ43nXqSbSI${9zSe9#Vq-@8Ro$!5hgYz>aPx&cF5Oy(?OQrZ z+SVh>uufo!jd3P4nkLO)_`0s|qQrxRZ?(<)C|h+Exon3ZQVeakjidEP9+5L^s-&z+ z%x2;_*{1RnMPAw+%txA;?K7_Sn!Cl^+`V}TTv^&F?2(GC)6qjMN$UDq`@VLUrM#b8 z!#4t}wQ<^c&JiryPj|P^C1ZN0#55L^;-F}@Xw0s?N?y|!X_q{pq zdr*76Iy!GRB@Hc^aRjs<4y89Lx>aWDo9piFMp;D^MNfvRelX~hrlr$ceUo`BQqZSH zEqfPDZ#4sLL7TdQ;-#XG#YqWuWSOPpd)s+Cuu>1+)vzUvzRk(EUZIF`^@ItjI(6Z# zio^*NU3Y0XX$ERJ@09Lk$J6c1WG(uMHZ*nC8J{a<`GaH3zg?z5BwEP%SfU9NTL)wG zml(X0#kjg6AzQMmU5a$K1C8hj*qx*8Nu?h4S}mt5Jt@(_HDsLN6dde=-rlz$;X4@DgtuEo-;p0*vb1NH;z*h?gn5oF_7#V3sUOCf7=Y-QJGsyQi&N zSC@@47BJ|bmZHtue$%>g;uv+-gDqH8(@rpP2PHpD6YOSPnIg8<0Xrsrf}^pV3d2Il zW6Y5=4Zm!k8?d>Xp&y8zvw<5*% z(rFr1D)UrK^6NQYDRkwpD~@AJxz)_j%!ll*#BQNPnyQm)(_s%YE~`VoMX$WZyz84; z>FX3a`W@Y~WbsBVB4ny^!OyP0sLZkaIEzhw#$vsQ_hD$HaYqz!ng$#Zu|x>hN&!9GT@u4HI3 z%8BN!H&$XIJPS_d=_6pDJ*D8xxG@8WYF1@A6)I{Lm=MkQYZscnzIyDX@#WguXCrHa zgY!MoN-~8Oe1XHFO|x|!WHXqg5bTo@IWQJMj5zK%2HV>=y+xnAF-rn5u7EV$#&zq8(8KE=vRtGU34Hk%gSP zrZ1iysr*Ea5c30Z=+MZ58DbX}t(x7$!ZD+}B`NC7`na|{Sl+CpRDRtv2&rzywh@_S z`%8m&F0nOZ8@afx+^aBUM=g=&E3ms2#itL^LyJL_(;=ccG1aByc=YL~C3UloWPd?Q zJE_24<+{hB3ol;UE!+zG;VAdQuk)C#uQDzRP*SNX-|@`2HIZ9B{v+03Ob72%u?V@l|~fMQRah1jZ6iF zZLW>cmsrK|3mj)H^G z4Y6_VoVfA5PItoZ8b#|pP~_&i-K!E_#yZ0dGlqpcIhoKGm$m{WO)4s(xTDQ#ix9P0 zUO}Y_Y3syV9)s)8*Tp?JPs8n%b9c|)_49T4KSU{05T9l-PMLW;Pn3`n)Y3Ximdy3c znHMQ_r9la4Hsr*W0krXh^ziDSM-Ht=XGXWRjKwvi{N?1k1x#ZExgkO*I%S!2t*MnR z$oF4G?p`Xb!PUH_s7S?p)9J16qTa7;jJsEJsA&pzS$ud))7Qe^L&4eLRFwB8P~3-JY*%v*4anKj>9&&~!1nIu6xW;NAonz(=jgQ1PtCnsw=(jcr8k#D zkd`d&u&>iIgtWy(wCiCDqRM7rkY=QlOMJ)6OtMt>y|d4)_pJ%Snig!hgQz=&Xi$yO zmAL8A6M2w7m?Ws8(Nskp)4QU%uE9bGBrw;qYk=+`@XL1@!Dhi~S5R@XBn_7CR@GWE zxgkbfEIBh}FE>$jQE{%XC6C_8f{ac@(>yNe64Ou~^<8sJobA(G*}D$pJxh4buFktF zQQ3%m5DZKq64S{=)s-1i2XV@zBNK|Fmy;D<-ralyYu=I@g5ejP*OR!9r*WA(ZO7*Y zINkE|TaT<=FhUM{oI`HHa?MV-r!}$0rstEp6sYXHG-(ugn@760edxR1S;EnU-m6_* zYQ)jrYWRA(dJEof)Qi@s>uYIdYCI8bHc~G?1Q-U1au)5r-+R(>EZ*&MB6Hm0cyp$6 zfl+&Hciss+!x(i7oxRM$LZRf75DzesGr6Rl49rkTnM_@(yh+bZ%k1gQVR=tmyS|m~ zCL)<%P_`x~yCPjy`Q1m5)~mZ*Syq_#nr^DnG^>J?h|8mH0n zjTfb~-89xS=B0J8Mv=L$Xn2N&hvS&{9*u}OcO}NR3$iJ!O96SYp)_oPnr6qkrIQGX zrXIo>5M9g}g^*1e-Y7K{LvJw!s^FU8(rR0=Cd%A*QPgsc4OeTl&wJ9{Po17>WwLnS z_S$Rdx3#F?x6i9+2t)>dWgtMnxs*ur= z7nq}w;utpN6(I#{25URJcSsPZ8YbZ1zJi**(S86?F9$We*|~tTJJok~VpoLw zR%wg1`L9-LHO}MHyGL&~u0?S2s!dKu7!wi)eX_(Y-YRl5 z)IAu3QhcbIk=Uq}%&Db{8HqA=c58mV?d4mPf-@7Zi179W=4L6A3!U--!enOVp3PxB z+bcYTIy=mz4>|463wO@LC%jo06mva$T*8thpdDchxSemeUtc{tcw<}1hI-JUz!-*1 zLP;I~VzKPL_oU}p8VPvlaW-!9^6mC8KpNTG)vYLxH+cbLR9Ym=ST+Xd%(=4N!qGz6 zamdAOk-Er1<(E`T-mRyzP_buTNl|BOYmN7H^?5bPV_LdSUWp~9ig5PwmNNANow}A_ zF7CKHLZ3wONC?hs<`Juobfc~HkcSYXOPDOWdBQWPREWwNxm~c5| zK|={qN|P`bOZ{*4oaFwyPagJbGVctbnf~)D@V_!x_wPJsPo?A24_IO0qOWUA5K?1W zH3kc5)(Y6Dl7Fj>wW6Xnlxu!o?&C(RQ4zMN#Suh*x9$9YIDZnYMPE|09ZG7Gd`-BrBc4D3U31w7?9k$@{=$?&};^E2!fffPzzM* zylhWS)v+qas1B>w%SA)1860>;Z#(I>zL^&dCiY3MM^r;oIL}&_D3RK>Z?dP;s=Duk~ZJTX>eeXBU=9-9R&216vZK(|Ngx{j^R-TDYqW%&Ie$!lMZ3K85P{)@8 z)VISBMXEnqnRrGvfoZkTZL+8gY(ZbE@2QoJZV)1pGUF{3`tDSwU!z;H*R#zj0S3g> zTW%otb`zs_V%$L{PKE7H#%pz zz=fA!Ml!JPUNSp*%`!2p9>W+I$Xa491!J7Od94^dB+u6$u_t=ti35^VP@1!a>mp#0 z3}0JgJP?KvhaJyJ!5tZ}0l9J6%`xKG8z3M{PHyS9=sOgH1j1;)fCeU4bQg(XOVyLfI3#CbnqD%9Ry!rPW zl6@%Wi=!>232mLZ8e`K0%n2*jB(b-zLV>SaE^Xb|q$=3JhZHTgEB0%RK4o5X6+skY zj#*<*kGOctwU(3HIqto~cK4%a!co+eVPAaUEA6$aqk|%GG$%G??TJk&gB4@#ou^Jq zzSnqOX5O-_gz6Ij%53?IBm)p6Aj51^Ob>gFdnU5Cif|6={m|Z80zd;PWF`YK&UU9@ z-0-3{mvpLCN51ZKz!G2?!7izY>6@~ z;^deTv805BV?+w01wlt0U37}=p$vrENd=MuQ0Ym~ZcXf&>wcVYFAH=DHor~Oyccqk+E%LkAH!9aR~bW z0+N7;0H8vIqyvK>9RO#NP*Vhx0R&MKOie_ERaG$zMIk{zQ4tYUMOQ`vG=&37(NqXb zrBKm9Lr8AjxT=TKuqlKP_IekBrw~Bl&n^gifhZ9^hti^A2}$&b`3iJU=g;Bq`F#E` zEoc3=Z4oE^xpk%u$a-US?7mTYJ>5Ba_oO}Q5mYVCxaxOjkc#!lJF`PD7`(3Hp9ev`;n)LKkC49}XcopiXB8=KjsK*tp z<8;dgf>qn}dGbR+n0}xMC8KcZ`4tTZXTb^*no!TTOj_Z2bkv25&KWYas<+XqV`}SH zWw1!;)5)deHtZ2BZSI|Wk<_(38LDJB#K4;kiKl(nt~shY>#=b~9+jsp>BLTQY1KWY zep@+QTcXB2L_Jz@a+#YKO2m^xnwZQWCy z)n$2nyomRE9q|J#UL4BWUVglui`Uu7MIo&xOSTsWddZ_qrh_`Z_lUepbdmV$c^uy@ zY1J0>$WiHmu8pS!zZGtgT~M`bPz$#TF3cGUAs}=FwvVA}B$2wRrF}wH78Baw0h|Oq zG(mz2Z5LE^N0u~!gHSSUcyeH&yxD_lpFrGqG=@XLMNDM-T#>3}-AC)U98nn~HGQk? zz|0z+r>8aC&8Z2KPQi3&k48-UHan{~W|X8TZBDJlWZ1jXTfSb_-gcEH`1QWhGu}Xq zBqI*d19uGC@>z3E2nL6B%}I!2^`lAo?m@0@PZN~iUD1FG+@7A>-egnKCr?QveW19b z@Vd9bYBb+A>%n_m(i-cLMv4S{cOK#{oYmDeS-TlJ*vuM*!K}FG+Vx=t-7f>vhOmg- zjp59~HbZ6F<*ZYO3D926e-e{}UWDeWKH3sA!A!ENup>~#M581Z1eOw7uqarOFf|c~ zy+P>+rs-7+NXCL~Q*yL(2w4^_V7+lHDr32?7V2-V6kL~g zUV_uzO8j_-CwUP`FQ1}(+xMkZ&Gc4QuHQwq2VY85v|$mg92YlabYz9&T9$5CX6|6k z)F^D76s`HwEgWgbqG58$Z4u`^FAJzUsn*UGNz1G-;+Xaoxl8(M42oRXtkR^kI@6$G zZ(AaKKt|uL=0lsS(~+I(k6Y>E(rS}IZ7SBuLWm(yN7LiWFHyEU_R_L?Dk32OBis9Um^P|1Kcox_aC znwm3IxeU@B)AVu*ZPYXn;oX_Cx^r413f#y<2W(lHyKtOacTQiCWUXs%WSVBYM%C$Y zS8n;n-QK`Y#b;zW?Oi62)J+(s%$G}PA%VTyfP*B*v|S@fIT3y`56>+25p1)2&)!qc zbFJ-;CrJ-T2c?{~tyW46=q{4W+*o%u{dVoTTMubWo2+OiEXENU6D>(pp=r^CB4Ybt zJGE(+?%N@lD@Eu>8JPo^Dauiz*q%^aP_&5~Fx#aFMp6)KAjrG?Rp za~5h?uvL{}*&H~Y-CK5htf;(J`ReLE?N>GEGg~QiprWTuK}%t8HxxrK!bRM2)tinD zUw!br?~RXHbfc$(>%K2k2x5-FM63rT=OedUTY-Q{S576Iy?DCV zZD}Zm%oSX=oH?M((%O-IhFCL8TTqBnj*YvvXm}yyr%TN$DvFt}p;NXe+g72?q}au3 znjY%4tE6uw?Xy=oS?$v{VWrTET6S5YRa3a$OnI(kHdkf^IXh*cUm;$bL(MmRsrB~w z_lXBt=A?d!qeR@dTCK;!!g|^>w(RGsEAh)MfWgSep8xfbcU^8(h)=b5llLdrKv}%(cEo8T|CNPd{ zbs7$%spJlnZdJ8LwI&EhUc;9Q%k+@%Aa&;5j5|8A$<_)%WQQS?w%<-~^NH3NYw8_Kn5I^4OUG<>x)gWf?*b0@!iDAn`ha{%TFw!?;gzER43INnXwq6wDb`?!+LHATDt6tPh z^}f@26<<8Oy?IvjHjQhO+mw0TGv41xgFIB(Ie9q+u^AX6T5i{yoZ1En0nZcLcV<9? zM7-$`)WMV4*6GxELo*Y^`S8%Qe(@b%~&lZxiV!yw6<23q(!G~N++*U*W0z_ zL_*V*hT1I=YoaB)Pg|fO9Oo|u_b_RBYrRWh>(VakP#Uei&CMg+ zavIWcdfBQdpR%N?$ChU2aHHitCF5VQgF?c!(VOeyB#!Fb{M+dcifiIC$aQTU9j~YVrAUoW!;upC54j0^5I5G z3(YX2RC`f{7qncDSWbdW#8^6>sXp0p1QVEs?9HgBsjsqxR8{I$=cV-+h!YB&9t?mjXJcd z6AKZ`jpjKrVWAC^ZZ>xsq1qvOoz*kwb9Iz7eA)Th0n4!HlM%XDUs$+H-toSZj#JLg z0d%K`g%m2$G`Tdbe=OSaI+l&uR!m##uS-;`{l7c_C0L{4Y*bS;YQf=MJ(>2L)!bYM=5xsJnwhw)-wl3W_yO&#?To7t*M)|8< zvDIzD*xDM|s4OgA?zf!bySg=fx;JXU8dEAnJP4!&#ij;MDB{yzX2*1aOF~3(Y%A=` zt7)?}#(3RdmdBjS(!&1MjBO*y9%a)C<6pdF3$nLGLL#UiL|Q7i{!~<|b<2N{IIR9{aRR z@fNs_4)ZjU2=W2V-kI+wFmpG7>)W^xHXP&G-rr-+8sBV?{K@?Sg9sx9KTAR>V=x3l zQ&@n@jgNOPlASO6b9YDl^S|qd^k=kZpy$u%=HGLBQ?0CRZM03TonspXwNAz}F&Z{6 z`{8Jaii(z%Sle69X`qZ%R8?2cf8DzN_xW?H*4Le_a~^U2aVB0nj|;8L&4S#u zG6~UkW=R~xG4m}uOXG7rr*p(C@+V<<#BS}pp4jrUV>H;V_1vWQcXz7wB9o4ON)|K; zriY^9%d|;U-?LP~HQ1&zr*wTp0ILv)pdjajzBx+UFmV%5nF zI;^V(gq~t~)|5=?*)Ycqz-;?JXsExR(f#)3+Gbzam<5&EA)%&DP zGvLe&_;^CGCX5PZ!sT3y+A(&^`g*-ODe<0&dg6#s&f%bt;Rc#F5+TS)nT%TM^A8D2uRZ8d45hNGJ$0%x#uomZy`sP>M!# zjiBO86lZ!v1^J|V5ob=XYc{dBDA2XtfamR=9mAK>XP%CA$)z+(Xa_M@VGmbBI?&9ns7RCI{TE#e~djnrn zxKydg2*$a6?koZeMT=}?NtlPVIF4Otg!f^UyBK3!s<;>K-s6mLoAbu9x7$4_Jacks z`dL2?{306G-E0#>Zt_2`&(nl7rIbnrMo-D9DILAru7&GaMD>SFWj8 zPQZmAwx=a`Y}vA1+LI{Z5*NCrUD64Jd1X+kFa!+`8$hp_U>^1Dv4HxivLxERk(+Ih z$TL-g5LO2A=t0fT#vwbfmi{$wVexo1A7XANhc==c(TXd!0F~#AhMBRd4rV6pk)bvY z=NWjR$U=CO$|2ij)rJN>Ox0bUC~25Gxtlb@<>zY@5rU9qH_WzMb4dVpc0`KuvbD zR=7*Bl^|#gaJJy973x`SvVcNwP5XG$5-f@{N={wR$(i!HQdu2D)$uqtZI41)y4hP_ z&Gz2B?^S(7NK(im12w>V>7qM0j&gd`6>RNxxjQg15m-ydcNUSc8CS2p**&wTss<#Q znF@up0}zu07cq@4np}q4b0V$VG}4T)l_3ht3vSX>+ndLlJ2F8u-foo?Qs|c8mZNKh z9TY4i16+lI1eFnZ;&$UZx-;~2yl?8g+#1uaN5CjVEyc0p+h$f7PBgu)$`22eHJj~S zL1=zgou#>@MG%JDx;5*lwgCm%tdw@I!MN$yw@&0wf_*#GJVZ!W<5dGwDMbYdLLEO- z<-iXFNPMb_C@5)YDOwhksDi$rdOSGx3PcK0mxz6jK&KH?fy|(v01ru_l0fQ686lqR zZpq*X^&)`3L`T^9Fdny6@)|}eC<>;MoE-PVvk*{}lqFEo((pOcxK!v0_XvFcj7@-i zg?GE*@;)cenpa`}Z~Oo3UiGy>)YrY*Fx50$d!bX^XECw)ug%c1k;?)E0^ZyW1@*?JI<1G zj4(A;(g46D2>>7?Ey`8}EG(3;zguz%o!wjAs%;pE=`@nUP0qIM%M6ieDFZ0rk`6S= zqh*DfMive-5bh_qyYpT2hcG&}6%;b#271a|*;*!LsaWAeLHg;Ww?e3})e*vtNI;kh zKS4G1yfbBDmh^_&#n%8w`a4ngtlZSg~GIXMKH+rX+@*a z&F3{X;!9__n>GaMTVsl>>dOZ-77~g=Rzyl-#suRcKq$co1X&PB;R&`WU5Z5>#=L6; zRM}(;G6Rq%*g<9?N|BKyhJ1v^g`vlZfkk!7rdH`%m>q4XAQXbt2vJG#+dNmL@sQd+ zbHuZllc{$5w?}akt(KfjQZ-$ZRvc@U4mKI2;&W!r;}~>ZIDo}s*a7anVCQtSWGGu@ zK}N(f_spkPrtg;SSc84MJ=T4?RTInIuHAYU(QekJ)(Uhbt#oQ0awd`Q1NOZzL~KTo zRZGHEN6thLY*GU};0LO{>C2^w#nBs4H&)M98qpzJH4ahg?Jybn?E*$%EZpGap zbsFsk-s?@4*?p?x-4VM0p+YUfh%8pttAR*@nDJ~$U}UVBGp?c)!E&&Pru14Gjgk)5 z8@9=n5)4cpyFDo=h%T~|q}>vQOcmK-q!5E_(MsE0H=hyev=ivAzz63N~2V^GJMP{KC!vn^SMu>X`m`p|@w1&o0F_!IP zDsvr}c8it3%KK9|Dpf^Bn zwer3fjYN%QBC4YCBhp-7kjry}fGkyIIOVHq~XV!V+?H^4FUmk8Yuw^Q9-3t@gxQt5UmQ zLPTXlXx#^KM#&;-Ce3$IhLU#Qdz}ZDjg`xyJKMDKIq$YX_gX$yC5{zZZ6Mk#74w*^ zMvFz&a}q6q?1edRdUUzeIRx&TrO>o%X!flJYH?kY+q1T6skb|=+r5sJyHM0}Et9Uh z486KN6tBC8`KYEVm?mXl7|=;hQwB$F<&-cBVA+<9k_bv9pkU#Y;Y8nU_k8cU&u*^W z2X<)8z_te(4G|MjjmGA-9^KMK-T>Wj7jH24z2dI-irmwq6P#tQw6#L}pQ~ORqd0m~ z*3(?>Xzxa}M^k{ba%#*wqMgCDV@+dMF1UgyXL-rH%*&08eMs6_VMt{tv2uWEws20G z%#gyzy^dHTZ4xGg#L8`>IGba3H?Ch0MecoF0Snqtrp>zr)O6z-ODhvMD|ajijq|>5 zbvllfK^g^L?h~)mRc^hi=np>bi13j@hG&? zcJ|4c?aW@ej@()80myX|CPdl-3yf?`#vx2-@+9*FYJ${~+b&{(?YCm; z+39A~7O$nKC%L`^c$eOnjW#akcXiiC}=@UfF>&nV5DH#nVX1O>Tfj}tW04&62ph*m`~e> zEULv}Dw6wyciOA;kGQ_es~eIKSW&*$8P)X7hn(%y{caLNi0%I_^~joUJlG^m`GqRQyFM+qcug>A82@tvw{It$lsG zU44=~@szK<a`N)i##GV1URSdW?|CW(hCA-GJGVx1O`Y$3;~BZs zeWFv+bKisXV87Xai;0D;8vd5WiiwFvuRYhDA>&mVfB6*0#RZ&Gz z5pYtfDu{}xh*(H`rnmg_xiC~y>j*uMFpn`~LLkxw(*5PpQKiwy5{s6V5J(VWU+^}h z(>R(*G+nucE{yqiO9sn%af~6ACeXP=o>2&j%_@n~G%I3p+z02Agle2-Zs3M0Ryk$F zt_)qmR52?BE_T-)D00D95k%CW?1f~Ij9qgXVS3|aOR`%$ydG&g3@C~^VUsOV^(-{+ zOxNafwxjaUjWeH*R_2z*nA!Z~&}VN*C~?G0@xi8fGo=(lBMMr2ZX1HkRzo5LPVLv5 z978PQb?II8$6YXeDn@7V(vgYKa3TXg3%ab9js5K1&;*?oKz?{Jj%|xUTle1mp3f;e z+c8#!^aw4uo@`R{)F4J8E;a1BkRVuyW|qt`rM`?4%S)3+cDYIu2@Sur%ufcc@vnl%wkZ)8tOpfo%7`^;1`88mQ8m3&GR^& z?=vRoq!Cc}jk{jlf`f+G=H@dAsZokxs*R|E(+$dw8t0czT^5$9rD1}R=167e3q&*w zvc$T|LgN@g%yg}xc;k7leGN5b5V7TNCMIU8MM_125v*nUA@K>)lZF&e3>&{2EI>jTAU6(suVE_??Sl=5R+;4kxwS|Hafii+g6-et)6yr=;~f;=NoqfTr6QC z%aL8%^Ki;|aKen|lR2e=&M~K#of7zViA}+&y68pl4_&0Kj*YUf-rdy$-)kyMm^gBa z(3h2tM-Lao+ZNYwRRm2-WNC*|DbKYZyt6pX0ie;5&*Xmw#Sc%ko>g-3;IiyVFSXu1 zx5L%okyVaq8j&eQDy1^fFmo`$X-uC?y`i&+gw+Ckb=b#^-%Tb|Aq9fMZ&qE%T&Tpv zS8rLl=W>zjZEELHG{gzu@L&cz%BT*~V^GvZPRx3;q*5qaLQA{1VGRbA>8fz!!zpk^ z1xa(5VnbOAZy5y~FCAOW_HQ)-L_}i{Qn*<{L1qGGW5Foo&5|aUJ+u{yU3=2Z-h^Ap z;ZtJN(iB!(+iW=6)~6wrjZHvAsyDS-o#Jk1q1{?pp8q{<{KAAP4%)f&DT9$Q=NFuKYwvQ0(*o z_3Mas8X71bb1B9OQljH&7Tn@4KA1;^=l~^8NS+-yuc!~OeWEC+JuE*kCdohL{y*6N zLzDXdYyER`Im%udD}SS%r!$fDt&Qg>oh&3_3RdNE5niiXTUPCc-wRbkQ_96ne9jK= zY<`UJMM%q#TN`2A3MKG38wPBMP*^hH-Lesd7TAU>x-uj&Y?bCc%+e8RpQns`rf3GsU}>9nC~m%G?y8~$Xd}awr8mP8`@6z-GR(&5V0mDXEZCY}(e=YiJUd#Z$hDY2?=h*AG34*0ml??ty2v zh^omIi+g*uv%C_O%VI>9wt08W?W-+2l#6u+f+2853)MSUmAS80zS_J`Zy@5ErEHx! z&POc`6POVg=2r`43p=~FV50Sg-P@T@=x#jq$=TOU<5hS-$s*_qD57 zg{6S$kyR-Zs6~$UsSC2J2!#4XZp8%)DKQ@DINFMVu4)%HSK?SMskA1p9eU05WIW(c z4Gv_AdIOugo)<=4r(F|(M=Tjtt;AM#Jo{RF8OYDVF(lQAS% z+>Q~t^CM9`17&L#+a#t5mcs^C@lrLRhb4y@L_J6qk1`OB6s-#8+kqUl4Iypaa~L5? zQ@ds_iD@Ue+O2&sBvIJjTCzu4r>Qo#Y`eOkw{8lmhUYiCA+w9M&TC?2-g5BKzMFX0 zVkDeE+OLg;x31_(6Yd*uG<`T+{7oNwrZ>CNNh8*!+Z5Ps$hyl*(>fYR=TeeP*v(LI zkxB1%?#iRJO`x}Oki?k@r8B-)k<-~S85pLKl z7zbkCZ<^m!+qUq4JtC;6 zacx`Ns9ew)bn}bHwH(5}w!4gsY9*k&n^rf3AtNHvhD|L^C5o~$h}4xC^;F%jL0N{) zN;hz2nTIfLO;@9l7}l3f^?jS^X4~B+oK@NC(YhdJ$bh1@5f-m=<)b^7y|C{?PPa&5 zdF#7;b$PsA;MBQmM^)V;_Z`Q1Lngj>=_A#o_A#yXo#(=L+fN>tT%l2JoNzrhrtKP5 zrKuzIuGXIXn6A@u6&|g?o>c}GCBaZj7G%Y-?;dN*UCT=!ta*s$%IlPay1szO<-yV?=DAq9rG3-P&t=aDaKRI*T z3)$`-?;9_to!Sna=&zrO=h>>-&uVU)TG8AH*Ktm4~wwUeGlko;&?!#3v~}G1YBVq^*jgsm8P$XKYu{a@7@8 z5gTaBX!}1KqC{0hRQ>;H`rqOH8*}-&!}&R5ncwF3-rkv>JM7DSg6Jb1c|^nXKRH{v zzGD5OU{HOrRTV-A`y%rfy_^HRWL3TvmL60>+@4+m2z!RK(XqK&waxV#+3fM_$WRb9 z+bKbE^}aQTd{C7|B(+2<9Xnuh$;r(Pn%`Z>UWZKY4TB~D)j9`=`+ThdIx2?|6QaEK zPI=B^A~`hXb)pAm*rql$Buqg>Xlv}+DkzGBgu0+8#+?IGuCeAE5PrZIprV3^mAw5qFHmo2{8JcelS_{?0>?d2+S{&C%Y z9e1$^7%5^^Vp0d!DJaMpk5W7&y*iLt2a*|Xqj4XT`WQ@>dV#2tXwUC5J@eC zdHfVgJQJ}dwC%WmPWo}(-!<#rWMLX@-u=-l-%o!BpoYzeY3Rr*QA+Imn=aH4s&OUR zHMppZ)AV%Icc80lkKJzOlLQAh9#6*nKGxfOfk6kG5Zp*XMD28|Tk&Q?r!h^4f@QhA zN|x$(qhg8VecHc^gdrGtF9_FlG&Uh?TBEY8l;1k3i3daj{;l=N7R%*RDE-WV$~=G* zdb@z}fRl#Df>QMhlVnq7S1ayZ-Uc9IqilsxGb=W$ORykGq}-QKS#rWHv5{EoVmI~e?lFjB zXn^u^)w8)+^x1-3BtTZCR4|oV@vG|smNq8WWLv(w3Qln=#_EjSrDhtX8xfmg5gHM$ zFE@n~^yEz@?@RP$Z(g2m8c5Sx_M|0(5GP)gtWNEuwSO+?aXvD^zu2aaf3GR zI*PM(-|Eocxum(U#0fEY`>w04)~}%!wYRywZSMWOLw)#k``!%E9B>gM<%YXnbl7Ig zC~3Gt7HT%!ti0n|XvM)b)!fc+GvJ}nW}<>xbyaGp#?b+>yhf^-ZMG(kav_+QxgH4$ zFfy(Y8Pjf&f#*1+h=w&~TWXbJ+=Ug4sU`+u*JH%SBJYb`1PM%&DVLdu878Rfa4549 z!U=G~RC279g|6I&rEiL-ZBh2)?a4H8MJr6OnOK@Fx>RjsCc=BB*JZ9bYE)xmJz<)@ zfwpsxtn|u(?qqGUBL~%-ZF{c)PWN`+6jrO8)g&jes%XbES~-NUE>TnjidBC!lM-x_ zByKZIKNLEN>4f;4529aBSRvF-#67yEK>BVuB=15Hp+8tdq4S3DB168vun_xIdObZJ zj(~a~JcL7@fdh-d75Dqkw{N%LT<6B?^h4=0P`%(C;19hM;-(k~Kk5HOCI2*9xBhu! zfAtPTtp8)=XZ>B@&-Vz7Es4-YtWGtF#jz6;wXHO*ANLz*s@9^5Z?ha?ilT_BF;)Bd zH{&UV?ZXij6rXKz4DsDDy4)zCqnCe(3_^H{=txExI-n8*ETl|S)J#YL;0J%bY9cm% zr;nfxd@xY4gsu!YP_Wl`yJLzRN0|e$u>H18&6JKQ&D;Z+NGY4U7TFf=t6ExYWk^Z7 z?HLEQsqnQ$v3go~*X&~nGALlg3LqMdF&Z+vVwV~eS|#yPqTQF{aGC@V_2~BK3&N8_ zlVEi2OL(-H@y?<<0V)RzO~wYI<~a?v>5~u=0iuO;L}`-aeAv6U70jfKNu2n@XYkZ~ zefUgX8{2-?j25 zRCAwAZ7E-hg+ZGQh-uzxmCsG>ITkBfi^k#sMf1aUWt`-F5nBvqJC6NrOvduBbkd+T zMr1;HRI&1XuYP+Dr#W+SYhmQ#Eu|#ORB);*D_gGR2G2KivTke=Impr9SrCX0#I0%AQOmA^ExkKVo0)-{iughPh3r6w-th$*{r%~`rq@(m=Cr#SD#2LceXe_N5UY1BaieUih9JS7@K@py z#h49IgfWSW<(u|bYf7g#eI2&syh-k{ykN%xMJ~cD(TIRuST%c0-JE^RHmp3`5?HRA zWHWT63gYat<6ZcFf?n0a#05i8a6)o^o9*mwA}&C$^!aH6d))OVx3l4S0()ze@XZxf zl~>kdKMXW+X=_nb>e)<6j)g80Z56{mEWdQ*yEOf^<+F(VNcQY83AH4zH8x{AmYbe# zA+JaHvSB0{d&*G_N_tSc%v zZVp?ou%Ng#H>bU@z# z9Tr0&y9XfSbX8FNTS0a_tvMH*}T!2|FK2pk0h6&L7)e7^tpc<hLNYR!tm2?Dz zfan@P3PCJoO=JlW#4R8JNDT^&_jR7zr?5TIgKl?sf~*dhu9GQe!D0fG`z71aPVloW^*s7;9}MjaRc9Zm7*3Q1k^!6R5YVNlmJo) zQi2f>6@suX8}kQ1!850M?%#C#EWWZbBQQGvgA!7R7%7xAwXr(hzbY^QP7A` zPywKmWhnxwDv>s{(xaiJQgKR{f{>yYLxo);G>bwjfOHJX8KNc{p?H!kmZhYiOvKK?NRci_NlZdGLJ)&gnRFq5h9h8L&P_B95=bIy z2SO2nYLUT1m`Yazp)^Seph^Q0hJ>XWP{C}avOy%s1|=H{KxbM5PC<%9l+^)Cihz~G zDN0xsrivDY#4H;wErci-&O$a94wFQr3~XV8MHJGR*#krx#4!$s5;GbyLO?_fz!f_v zdr?GA3OoR1MG^rd7x;h@5r7;j0slZxfRY4(C`uBfsVGVkvakdJ2?+@aDJcsJ2oQlF z$S{l}3c|7sg8(oC0KhN|0}4Wtq^TlEk|c==LXZptDoTF7gfFKA-OsbwJhfx$n!emKbV1a-#k}Ci*gaUui0fiiZ zf1m*(u0(+Y$xl=OX<&v@kTiuH5JQ8xkZ1!0A)s(Uqym7>lL}F?22vvSjtmy={ z5YiNyWgP<0QjG{=3B-odv>{Ox1EiI-BbeAiBq*aOR1^s4a*$|NkxFp@Xd1~>7>X!I zBrYH*D5VN10)(U~(L)plz?8%&8fC;GP6X0Y1|k47l!hG?4oXFIP?Uuw7&Mt_(j!O| zqeX)vXbJ?wFlYiGp`|hsx)35ll_Ca)k|m@PoGb~UK%f%@z-U6q(9o161p%p?WeO6I zr3{9K%4yOQ0#F4~3!M%C(1}6`K&1c~Av7pZ2I7Pm<_4NUrKC{FI!2m6C<=%gQK4#) z3P8~YhLZ^B7LZy8Oo<_-X-W_@)S3XOl%iy+2P#Tc8d^|dRHRabFfve;peUe95`hW| zq(TTN0+xj+0);SXz$TKFDJc??s;LGlMkHcd3nnBq3K&Qg(v*nO5g?Qanc9TN0i_)} zGO!v1F-QcX38X2dCQ_&pkfdlb>C$L05Y(G40woPl?HG_? zfS3qiHH`t72ALwEf*2wKkbKc3AGiPxs;Pkim6SpdkN_Z6WKXJuiQv7X7Mv!U&|=aa zXgyU2C~zWD2s83RC+(m5y*(gqe0#FT|0VXOfKwqT)jVS$NYBwZmuG%Xk?T>#S9 zT0=6(k{FOF29TlABuIveDOQnaQo!ZXl(`@@Ee49#ARsXcv=|jkHVDyU8Zfj$DzJno z29%&sC?Zv)Q4Ez)DB=W(8lg&48c+-sLlUlqX<7o1DXKCW27nr{V2P4Qhy*lDgF=)d zr78?F69Gm_XenGl2x@_$C>sla4Gt8~q(s4? zTVNy*(@KC*E*%7pkg5tASu#1bV*ybHur+K`l+d(*VhR`~G?6qk0Z33Z2}w$nttmon zCK5&f(iAH|xFShO2N6P)4HP9U0u}`#Q797>n!^-G!kQFlsQ96QVv}vARrwhBXOGa;;)idC`{3Q(blHHl<23S?bUg(lca12G_( zs;E#{f|LPe!02=tO#wrls|*9AXc`ic!%L$8<*;nzimHc5Q0XLrO;Cjj8EI2fAQnNy zkS@wDk`&b_lnG%mp>k-jhEqiYiIfeBD=V>`IA#E(TTUe<4Ip5N6GFKfZ6reE(OE-T z)t6Kx%OM0YL^Opu)EWv3T3C||QJ}y`OQi_LCDRd7XJOs1(kGC={io$w3mXgeXcH24e~uRH3a9FePh5 z8XTG%1{*=8))HMMB`LJIU6z9p0_Y5ehe)*PbQ(6Dp~`8@TP{MGc4=tP!)RR^T$!92 z%rPWJA^hS2_M(WADj;+z8;s#Gh*FutQVLL%4JA;KDL_$6MJ-VgL_k+b!D6anO;tn{ zRFp*oF%?20>NTpQEr8QRK|#Qz=27OIPF5d=y^g)2a5G$7JMZKh%Z=Aj@?O({ra(y;+WP^LgSi6}^c{ZV*# zrUFte4VB45s!Xnwq{Pyaq^^Xe97;nF1E5nYrNR^|0jC5K1d%XMI|ivVIv6DlQ-sDa z5Qc)X%)(2cvJ{I*6chtR0Yxn+Vl-+O5+)JYN-0K@B1FJj0??{hr!hnhfpCyOO(CHQ zR~pQOKspk_QW=6kkkDats1yrGr6^Da5*S@Mbd*s^Lr4@ZLDFCoL5R?5C>;o82zE@< zr3R2JB?cXu!07-f3KS{{1;Wgb1|>=xCV zbb&@-(6k0kEtJ|E8UlnG7Lf`HC>jB9X~Jw!k{OMZ!7yxs zQ&iGn*((N+p)lzLq-zotHKbf7lBo;~I&dbGC<;uQQfU$-k|IKaG+>})NRp*A1jQyr zm69S9(AYvCqRvetqSH+l0;IGs%cR5xgwZQ9lW7bZ9jX}ACeeY3NMwN0#!T&HGQ>(k z)KF+LnhZ+gD~CcF8%7F}LMBQb1V(c-34=mq9S(^C5P}OyKvNkCT$-TD?3-8(nlhL+ zO$G#%(}EaQh+QN?VKW0t=2%S>D`iXx07B`?ROT=`T6B_7Mw1~?X~Cs1RZ*lchLV=D z8g!17Fd9P^gG7mFX)zjbQUR)^prx@xf@vm~MqpqWHo|GzX~CeNt1TmuG_;mV6D*8L zOfxA2EENeU7M#YE#>y7b#05JhqSLk)K-by=oXN{TVn~>%B%(r=m?L8rohF=0f5@H! z2fP8`D!l>_A@V|s8W5(045^?gQ!AvVm7&X}&=`a%GP)L&%4xHNF(YcB9H!cVG_4_! z2!l?Tf^>xdP?$$ZW*s4?SU}Q-9TX`-X$By)QqqMgQA&jv14_GNDFW!x3Tax)rKKpO zDGE@^8WbRaA_E}l8Yn4LrJ{hO%IJwesuaVbkfxC^=~@!764FGV&a{yU3!pHjkf9n- zC|3$eL@TAhX=wu}X~iNG`gGoV2NaTVU zfK4VqTgd0u`E@>h` z#6c?15h4UcLd+qQAkg7pk|ogOfTWm~9V>*ENG4(`mPc98fkFad4S~WkA*4*WsWC(j zoPy~CR*En{q^qr_lL;{ZVpf63=mRc{uo#r3TLA4i(o=wqkZOfZpbeuejKV3Xm13j< zB^nA*BNRE)v{31^xggM+5HSH|3|h&tp(Y9V3HwN*CwTNOqDCTlB^X>tv?D=T(rqT8 zA<|Fw5`B4}v`6q8jtM8G<6T1=5eE1+^F&8tShktGX&C}4@uq!m)ot&qt9 z1VCm04WKcy(v&zEK#_E&4J$yj6fHC*Eg_UK6wr+VU^EnzG^;_W14@*QC1#9mBojmi z)L}zFQJ4uB9SMyFBw)}jn#nN;KWLsA-A3?V>hz#5=H#)Qne zF#$v|Y-pi@fY21QCTUnS4oU_<&=_E*1c4x#O<{CaVjUrch#1ls9V?2hD?k(mO(J4Q zf!HwU9E7?GDagn~1s0P=kvfVZK=BIZ2!JZXi)5n-qb`;tG6Wh2X+uFS)YS=+05zyd z4Phi8nOy~y!f6aKH3^`|$m=Vz;n0+rtqnGyq-YEVj5JXLq@c>^a0Zf`0j;FOq#+2( z%`2)*riw!nKTiU6RaMW8Iw4qX9Dg*3PgA(ucDlw{J zuxX@RhJ-67fS^+pM2QZH7g8h!g{0J@hzQCQsYyw+xk(KPSx8yZWHhEkiHAW-_07zp{C4i!r2F{5MsdVfPRD^)i=!7N; zNwg*!Cq`08&;|lc4FRerRJomLrPxe61qB9WWRM_(%mM6(3H(ty0(S`jdqoj2s=X7U z7)9zJe~3~1Kp$YL^b@KOf%gfNMLq%uFo5wCMD?g;3<=_(0D~w1ApYS;^#DGD{Xfrs z&5i_5;oqa0sH8Ow%M>tW%P}$kegCKR*E^LDyr-Cd)cA1@u+%DX53T>I?`)k&NEVFKREoV||8%pJu_}8P-{*0ue z`cTHkbh~qFwq4ormt&nd6^uzSH$J`pRs8oHtTf?2bdQR)k>Z>*L8t{Vn{-QE9=QQP zD8*&DrY0Lj)JGFB5nZ@iaLh0@i7IBC zJZ26!-f5veTmXb6yIbqWA)gU33UG`|ewFcq{_lgUR0VNFA`?VE6hiqpVHu;28X~9v zq_I^tdE2XPZP=Qx(e;y>;*~|Ip;}uNdu~f(wL@D-Al?nFw{`3P)3$eu?{h~$VU6pv zVn!g25C-d^qNW)yt(rrY+g4J49_ga~(tLf8@bq6saR!_C#SIo>ernYjEonn0SSx5AL%r@EWfh&Y=KzH<4Ytf~dWLe~%+L`=_0W;H>_;JnoM&|6 z%I8YHn8X}xnr^CO;zntK5i6dP6ezU8y5F^FZi`O}gTL#Zzm5o$F+oK5ZQ>6oPnad( zJ#W-_@nj$pDF2&C} zcSCw2mz8gl9y1sT>ln5gN{Ylq{TV`fv5jxRJKATJ2G8#n1-aJEKavY(WEet?+IIYbEB}&3QzcP6J zPT!ao=2W)@)`3Pd$^W4#dsAI8=4iY*yS`=W6 z8-{N4abhmpBFVPtm)*LY2AL?u&r@(xH40klXz59D;DiZ(cJeE-x5bSw+~LpsM7%L| z*tmat=E^5$Z8500nv|>cCfi>(cML2hjX;7TF(z&lz{03NtUy6kF#}wNUCOz*87+-y zeQ`0EktPcub&aM1=K# zKG6a40RC$Jzz4ts_bR7Q2l;@fa3Fv5MG-$Bfc>Z*l$J_qsHF?{3OxXSx+IC|Q4YU@ z?gF2n4w8_whz$u48$BR!#kYjsb3c=SNz9b_k@KB5hih(PPR+lPt%v^_{-ZZ9_u>PYZ#O@y2_5TndlVN{Y`k=e+HMH|T*2 zQjox3mRI6YZ!&Q;|Jk(ITADVu;ZDDdr$fG+g$$&R5wt{cwxb>^g^keMK|B+%B)wrqhCiM-5HF&Vvow<%}_ew@A-_b(Yc zfy>hId<_Yo}R_nPY;+(EmgJZI@W9 z%nz^+fgeIrMwTh|PxRJq&g-V~#r75$B)aD{J380&z=C$ZqqVg@x5=6Q@(OYJ1Cl)MN&JH0}F$0~=-9rXr7?&ax zTxm?vaXCfzyu^=F#=SJBj0&h*{^(k-5VNJR5#!DLyUJoBvHC?~0qb+*L?vS#w*^)(eA?tzd$fd#-0ABB z-P%pStPFLf?zS8b&DqEmcD|(P$|X?z z`+vtS%vtSS3z4-Lo_sp@WS+GZK|yNGky@?F1D3d~Bq=3iZ;cQf(syuQ{RMU~j zd8<~FL3CZsy9GD@g6z9P69%k2Fq{6qR@AX$ShG+TD-gb}CfGPiqOrqdG|W^xJS%JH zM0PE0OI4L)ky;c}48+L(W5@83Mv_b-rZ2SQa=}_Lfm)>dS(VTiZfZ1W?p!NIn5(Hfy3ImwREn*3 z+=)P#J9knyRKL@kOY)E8k#oI8xD}-(qDWb|`7O3NE z`jxC=dT&eM>D#x`TKL3f>gNRh-LE}iov>Kx+>#ZL)NXS(zV2u_J#fK1n8<0Fmf&#= z=MPPiUiNHq?nR(Hlqer0qfL3{>_DY>w4q*Ri%Dg+q2dIARNSsI#0U~@vnB&Srl#{- zDIZy-pYhK}cQW8n6^MVY{{4W|P}+v#CsH;bkF$z8YcQZx;2{a4`MQiRr4TA>Nrh^EYc{V+AjeS4aTY-8ECq|IsimRh zw7JVr2CZ+oRp+9Pv!ObaUtjkG}+Hb2fx{@;JkXWq?u+%rsHbE0~qnYpbX z$h^~4`%G%1y=}+OHM|R6K{Rm@?bTXs|Hk&^`uz_%q6PBMxyIdGWRIM17Pjb`pX;3G zv0*@u_a`tyyWIKn#8-KEm+EmL=Dy5Av&qPZ?$`6Sb@P^-PW}90?YovJv$luf4Sa`O z^$llPS@AXZ)b4%D1-##mpC7N7aV?|SamBB)R5vN8D65PM1oMt^;N)}^NsVCTDk5WI zHV-ElI}(LRz%r-=FY{3TsGkrH3E}|%;)C!5yb1J2+YJXDC9^n_8WSd*p0)>J!kv(N z0+jG3RM21@E>}Q?3_zNiRM4?d0bx1V2=(}1$LMeNZ}IE={}=Cmm-yWLelxG%VgD@y zQ26njRb~KbN<`a?B@ooETH;6^B1HTC2iOLfy+2Ifs*17GQ-9XHuZfFk7-f5gXIx_% zjVd{_Ng*^d1#+zXzMOaXv&SMtd&mzEC+p5kAsmLk_3HX9Vte0QmPDKK_qWevKe<^a zhS>)OeQSMTP#ZtBsEqq$_Qh&!$N~GZO62DA^UH}FN{T6cRX-H0>*o7&>V6r}oAmr* z_`_uD6h{t!dG%a=Fc4t7-#-_={M&c&^Xc$s-%!uZkjL*!BG1AfU#}Ik{s0yeL4FI& zBlb><@t!F5uKy}wN6)xglB$De=yrw{viwbznB%|oobG|zT$eU=&lBQjB#%Ye1f54t=TGH!PPQY|8 zZ79~`L%|y|Mg6NqrkM~aYgIvLMU^0WV|%x|w}F{0LTI5M!z@sCr_3cxR0XW(twGJg zj?sYj&TOV%xaosyo3$cjF{8bm2uJQUU*EF@s%zBco7&VQ@rjj*fg?~VTcINk- z2_w&B&7r|=AyG#Tx>~P4`Z=|Nvz{aZUL>*^ zKjm+8(UON~u42TRKqb0CTbW{saf_vSb7`ln)?{R@d6q?hv-`S#1$*9BPov#%^INo7 z<61|OJb&@3+RSZYKEDZc;v_JU*vC5(Q@!=?>l<72#BF7MTmcgtUAf2R%?fjU7=t|d3{M3j() z4ah~avq>dM!S&3jl`f{Z>TXdS5~p?2j5Z6~-FKmRy|SEXD5epDG)cysG@E7q&!XZd z1i7iiw98h3DtgGHGDRAXHW7;c6Y2X_<|U#QuMRd=(4sAg%UUY&2{o;`s9v-DbN7(= zu&V-oJ-<%W_Tp#_d4IODd%DG0mD|U+j@b$-qLM}l*!hY5su`J{TEk+rEcW^Tp51

l#Xi<|3dgP;vcuk??T5PFNWvfTD%dYGq(W`T0+|5ShS7&za zpXK3j{}SIKb)d=5_XRl>6W! zR8Y1*^&S~SEPqgQ^=|pm2#JCL`YlwG^W}xRf3S(C9Hb$rGKEHg{dKRdVfQXr&Gb{d zeOXtNXamU_ip`y<+LJW(gRxdv~PRMJpkrCHk$}yPvyi(Sb#xn+aa?>#AmAQY{^HP4c@|`_p%) zdqjgUXn2rnLMXe>OyDyEIFkw0>^CWdh=`)C&QlGkV5)dgrzd%t`SQ}NRzo7&95(xv z>M{N5qEt&1F`zIZ37GRZj$WT@rF|O>y*A{c^6D;YHfK%&0@ErqHSKBhf`$T-MS{Ud zjJA@#-Oh52OM-O4*tT6ubQ3~?P*|ax?c&z^FC&!^sO)szH){ms96LvQc&vHC#Y|NW z4)X2g-Og@t_DeQdU_}8`Ulnl@07w$8KA+oaP*+zMMZZR+?yGl6O=sa+8QPQ{#Mvs#x5A#yUT=is;u_7@t?DiG_s`^I6|hMXLMAw1 zQ6W&oh@#ez#MYpKZRMsy+X^)PlQ5!EwCErO6AG$G$_0TMKpu9_CtAjBX)SF1%j)XO zSaN2>G;a9PYoOT6-?q*P*P^?(`08%2TN$~N5w~%IkapPAS6GvGYaK1wu8QowVjQ=v z&y%rZrHCp$7BPr&E)x?`=inG<#Usc6VvMD-o|0fod9DC5(jU~EQ7G`D= z2+09KMgWO3PHyfOmh#z}aVcUlf1&@OG!Z&c?9JW3O;uQ=lo-?) zWGxF>g#gF|0}Q~Ov%GHkj3_Zfk~!OMSU0Uj+RSk0K__WOgHkc(oI{W0H8ZqX>*Wz( zYbcQs5fxyC=U;|dR9(B&W*o?wVFwkFyTlh#bYtOhGYrPDF^I>3EO!ejI2 z_U45%4wo?0kzBtSNWyieSlc=2uGntIb2I`{5X?vhz8hgVyAohYf`(PnJj!rA z!TjHsWzj#_kT$h3-B5P_@ZP*bL;YO;qkFa%-YINn{-t^iv=q`zY#?h;%=?)F-NX!YDF{{QLkbbi+rO>Bsd zGhoRhpxsHLyg(w;Q8CI)|1NdLn_bT5X%>uZ=9r*Lva>+Uq@u9MegqNzPf3AE{w=LI zegwnzq5hl4ds)&5pF|5xZL=3E6oO&?WLNgeCDrAvwDBAR1qH&fOPL7g1}Y(TNk2g z$M)p8e*S-#M@=K5;rm;kzmi3r&>5`{Aor^qp^{ChVH-YPw(m#t@8TChvg&R z-pqs}ac|{yQW5d`p1hVxq?r&W$96P*?{}al_;cfir2#p`llCqvurKfPbL_gtky86u zZ4tl4zI0k(ekbOvYv$BFY;KJVa6$D`4AXdBV3|YHX0)`Z;nkQv|ERX#=DU13;lK2$Y~{LV>Ihq&fvNp(s^|8c`__%IqDEh`m_{dgEnC;c0raae-`w+4Q|%OQE@EVcVS#v{yHME+;r&hs(6r{5uRei`2f$u36^9Xba;;oRDX0ru8ndY2wzR*uZ(ues2s-H zV|{aWpKIs8+uk(e`$Ee7_b{I&zu@$8!d{+B#{yr{34{;le%$siG!YT3 z_!1Ejl382p{lZ|Yzb?Tu5d`V`&BRHF3HE=8U&H*P`vfQD@4rYNi$ALRef%O4Qk=)^ z`~jl=Z%CBjf06Rv;~Qt>gZ}?B@5e|d_zIeQO8Cy3Y7S5ZK2hfYF(rk*GTeoTeUkz79yRqed?_KV1GQ|HR zI(99hBN6{u{MAJ{NWS^9nXB#WV(Gj78lBNYFiGc>u6nD!ifj;7m0L&{@YG_QOP1)i z`Yr#(@z2pnnf{?Og%NdFBkB+=-EbO;f3D$Qr}L<9mBqSW{D*M~ovfd|yM_a|XFlhu zbCm9OF3gqnymMveGnnd%IGIqf*6FA>P-4|8YrFh=b*$yqc?}pb6l!aYuQ#oB2{w+3 z$D1nawLCN*1r1p(S%);41)ur(-*jK%yXWwd0<`;mfn=|8<)81eCR0-{TECy$e0BmS zcKcBw&e)AkrV_hpC;6@$^~>ew-{A4bMWnV;I>;$gYycXsoWJ?i{>SuH-SE>tzW$Zw zlD;^_7jq;s32vunY`oHfXkX2FhnGHzW<_JlDp65}Dtaw%9@uvf>c*G~+zVpb!iEja z{!aUNH{HVubJ@MsIFwGs2sOB)F)dkg;~%6*7b!MPy7fQzeL8pjWUT^q8Z#{4s*@oM zpcWE4CliVBGSy>iv2ir8ipe}-0cMl6(CS%b){fc+7jo!J8LVB28MRnw>d5VC39?(N z*(SAUs?zHfX~yauZ&4$*L{3_cu|*cA8$%}9R)_78uQarLHI&QqyoWh=SUJ{SOGCP@ zL0Hp<*T)JY6$Hrt#Y?q%E{UESWq17Y!EzGj|ZAi{#^w;|bWaFSB>ie!l zUo?|ibV>8uz0A>8@BFo-Evv%fY!g;i3KLr?p=T0m%_S=o2DLP9*?+4o?z)`WHXm09 zvGE1UpukI!W=7g0-?slvsQZVQ*hqBbX$*E+qq9@Vu>WZ%1Wn1x*?Cs>J`2-#5u3_=FOR!FH4-StemFaY=$II;hCBi^S#9bLK&FM1d7BZ7QgGBm8Xrr ztUgW4ru-Si5SYG1%=9HSPt!QeQ}&?Dx}n$VfUPSp$S63oo9?I4H;tTlZ`aBrUDSoTmZ{m zqYh+E*vzVtBvKDauRMh7yy_I?#6T?@uzP1%;PI^K)U&4ZWRh4$3vDqVJkDKQb)`|d zat-dg8o7a>Zrv)CsGuW)qGST~-OL>~JC%W`8j?mrYBX4V{Zy7`vm zi;rwgWd8p6TX<|7wJU*MY9@%B&SxKmq~8~d!KnRxA6o{`uFm7;isSDrRtO9}#3m%E z5s$9c`I36>?Q7n6z}O2A6sV7qV~C9FF)@P6cLJb^jQg333ey!#+aW_6`V@uw?=BqLOCFj4X#oZfUC1@NApD?2X@jxNiRHRlB-GA^7t`A^ks$5V@E6J6OGasmP;^j2 zZ6+idA-djL)!lN)q3-S#)Zn|k;Ay=(u(Otk!B{C%RhZ_|2!NGtM)&b+Yz3R;oO+gI_ zK__fz9a6PIQ57vE6fs1p5K<7#4;Y=z=S!}St!o%o5dGU{f=t{ji0%5`9#j5RdYUN{ zjLEO?39p`+ta>S{tZ2{Ko#|hD%dN0%)$buTD5AzF$n~?00>DMeM_7VJ6&8wCDMlhZ zjG}HTAb=}w>Tq0mHDiUU4@q|PbUR8)M|r7lOCyZ6Cl>8^B|dkjQj8jI2%pK0C%so1 z_Dv;CNFv2ixBnfb8Z2FVT+OQ#Q0KE_Az&y|Uba3R89)Z`XCMIldSS zqKZ8fb5_~x#FstU1rd*ATd>{l=eS?W?>uZ1S8nI8q~Eug5)-ldDx4?yj7K zNv$jhD#04dVxt|A1Zvm~RbXr3=}H^K$-`U9?{5^z0%yHm&6kdy&Z^jwfxZOCnlEOTl7seFHLUuv07V@Fy&=L1bKbIE&JQQC_HOIL}vcWaFnJO7r| z!$4xIMuQ-c%~B}YY{wm~r{S|#Wq*5@!goKv<+8j5TJ=L-`pakVy#6&6jZ`p=sM3+( z!4@tFoG z%YS*-vKpVkUeY;__9KwA^DX{YvBvZt)@25oFhZFA)+N6)i2{FUhI;+}=Cm342Y`^W zpBDncgAk3Yi8#)F-4{&@UKtxD1k6}Zi>UvceeLzpZCeDwN~llcSC;gD^e3)f34H== zw(d16T9VD#vb>5e?v*++iuX>kn2dCw6PrtJQb6gh_1(=8=v(5=c$`wb1YFQ1(HRUa49M5<^&~Js7dvf;aq;M zmt>}P(qHUk!Yk)u`Cs(X(R^e4uoiUKH=nP&C!YAS0;ap%*Gqr0Two6gM%FzjYgeSNyOV5X19p`LpTPhG0D z9*H^V%uK$MI8v#6^=)fhg^iXg>n6t$rY9|eC;1kWhP~~-<+U9m<31*Ae?H2>& z11Iul+$XWM5LlqU^!>9B^o;%!>BI0h2m~Y-#E;F77w_ZUK<_+19*?nRe*UZ^e-J2? zH_87xvP2}05I-G=rA;@O4?jDA5*V34FQ;|8e{XpS?%NJR8uVK+0RypuKhF?di_8E&jiMcx2##0sj5%byNvlsciK9Gw@&dnfw{?{1@R%-bDn@ zMt{V=zl_=27?`dRot z^Q8Kb*L+9IRQ#-4ihc+jUcN2Mhg=^1Ol0~JK4cHgpL}}fg&iPAA`$#CsUG@WUlq1` zJvjNoPK_9dV2}iXc4uKP3t>REMo;bEe|=7*aH2T|L--OjkHmh{sL$^H9d~wj-&6fR zZhuy@o2%~v&yV?w;r9;LoXn18*NMM5nV+w{y*mL-5he#7{daxm{l3$`1`*5@4;IJq zN(~}tTSU;^fU-7##pM=E3?pHmx zchY4=e@tqYR&r4S6+SyqVsv2Cl*kM&Kd-yE#6jymmYDeM|5fU&&t@SQNa22!+2zOA z+5*P~oF9KSexHBWX}>ZT;1o=`nfTo2>uMOR6r9TljpX+FSnpRXZF-i~eH4Xw@sF3< zC&6Na2l?5BcgkL(7RW`MhmZ~r=lq}J8@?t?6C>d@Xs6C(O{VW-sNd1@^MB6LLh?^T z?qHDyj25<+19!Go1t+1T{ohN7Pg}%T$T5(H0-Dv40&Pa=L?TA1Y_-maC-i@FPS!6- zKWjHiZo14+-*4-^+gQZytWv9NcKTF?Iu}zwX?NztyLnY=>df7w?zZXtzSU1ED?Ojb zbNcta94r}b@UEIW92Jd!TH?RC%M*F=CC(jNn~#Ln>=NNtJV3g!Wag#|vPz@e+K7a| zu}OQItqS>S6p*~9(?5hqViz?RHQ#jA(Gdb+_T@k?(jzq3Jd(^;Mg9d8#+VrY=4am| z4;OTwbih;;`~$#IAR{y;DWLzD7VlVL#+L*ms7?xm#BIExhzksUCZ%<}db(P=Zs;Z_ zdQM4=!7UgZPDBvRS?hb4Bs+(fTblT%(c;QZgtBvARKp>|ncY=y74zCn6jcirX!FdO zxOv)1W0APDla2P-{H(vxi3!4Ho=?R>v7DjJ>n>D{FyGE}B2Ddkze(59U^LtkGo6Hs z&4M#AZ)a}hMO%reb58%6I9x^N1?LQ_btm=hbbo&|pAD5WE@R(ZAFNmoo?92TWL{o6 zAb}K^JA<9WG=->Yh~BMSh^*&0X=s>Xywn1`|EFJ6*I__zmK=@uLf0(rp`zh)kc8#h z@tZ`oyj)da|JrR&b+i>#e7#<8L%QmSPIK)ZAVq6-Vqfij>%n_dN17Ge{;H*IY!Yor z|BRFD1bP1UM2)7b;D_!^*15OZraj#q&Qp14XsgMK7Sc0~TZ^h`Q!^3LB%vh9spS(! z0%htd4|c<}s`7*@AeG+Gf3@q{$~DKbD+>6WoT%2KxQVM%|33d*MY_Ln4zI+YHFxC zwaXM3i*0B1-I-vomT-g!6Prl9+UuMAsXeHXIgNO&jl(=GGej!(&+4c|uQuZPw4=s@ z2V{>KwWum%uPN>RiQGy4PkEX&8Yel$HK!bnPp4KQr;%7Yg zn%9J!pYL;{pl9C}s*+HCo3C%%5HER_Hy2s$E_;LB@$PqFdcZ<6(cK7Ti$v|}TO^aKSd+l;z)co)Jlr!4 z&KNzII0IFxC?X^#8g|vOl87oBV?^z?*Im4>xuvjTxLVPQs@j7crZ~X{mel-VUzwky z#OJ>h!lt+~iR3WG+I++NX}!{7D~4LRUbujOKyhnHT05&~NmyOF29Z>R`j)FpT5hH( z5sJN-#Z9JsM2hnAb@P=`*5!g=n+qkwdEp2_as7I3{|@u_beK~|t!TE{GbNqXLsv-L z!to-xD&Ezp4X9=v1 zU${i$ zLoqE+nYKSU@t1F&jaVQn@ysL8D%fOCqix0!3yN7DbsW_e zxwwat;-xLPQ0jy_L=1zNvBDTKM8e7YlcCL-81wu_w5GfEW-P@X=Ew)rWtU>Vd3EAG zbU6{GpxD|4c3WstyF$o~lUgp^*IK#!-nZ!h()eW+|jwM2U}GghC~bGoDO zw?BWcF;8^I>rdY$6hW(=!1e%5haE6P=~b z9Y70c8N5#A(~2i1l z#f=~5yU)Ucn))YIUJ-eu$*q=3V#i_5;eaHdu{363f=6BLebJF=I?C9qQVwmA5$4rW z0t-U}URo9)dh(|hB(4Zb?$-X> zvtV&3IG4I)$8#molXl(LcbQ?X!HZW3VA#oR2!xDnNR3+|o!tNtS{O9?DSSPwrJUNH z`q{@>;%@EL7!@pQfDM(}hRB-|*;?~^9Zu2DmM<1qskce530sk9tuE|jjWigZ!)=9U zGVv7Yf4av{MChJ$;e@>2<$CLMyBVeg18#|$xB82YMcIArsGvYk*c-3xQyJ95szMXR zuXJUwJWh7al_f_&)^!Exb=pOT_nF42t)qXtWMx`Y@e=&4Mg3R@1_$zqrvA;lG(egb zO?I_Gt12-w6hipYB#%U)k?zFy^jr##iK{!MlIO^2VWo-tR3OYCtXiXjA;l13#O zfyPXE%gpsLLmn_jd_z!BpD4J)skLVLsqpYSokJf;l&xJXa+h0lBSlMou|s6u1UbAp zPCNU&-}-$P``iUgKax>8zsZZJkNF<_dTVU{jW|z&hxeEBFZFPme9wmCEq@XH{JJ;m zx&6&Q&+6~TPt5;94z@G&JN^AeenEc$;+Q@MeY5#v8}fV;>CIsu-xt{qpY8AK!Tbl^ zjQ8;Mx8qmBqFesr6Zj(egHV{hhtuTG_iow!)%JfE#i4&MoiORZf4}NX;e|EdqaM9b z-{<;oeecEnPs?2XeBE%tJJQSYh`idw+VTdtk5&eq)))f7g%rgQ$}q!aL1M&_&*}A& zndtR}Zefhtm~^|q(Gh|n{7L>D5PN<)ZL)M|}Q1&Sz9YH?`f%gJR9<%^c^P&fMC$U4;3I{{!eNSvK9LSRc z5UBx#rjRJFAxS zDenI#?Sh}NxtuNHB;#c&TPmt>!z%x4Wh2#IZf<-0n~l(*SpRR`JCi>2p*ve`rM0O! zK*AY>+h9q05s=rS{^h)<{ja5S)m{VsZCJ~OicI4BR{Xb2iB}FWIp=ZH`78YL8;|$j zZfb8nQ={0V#UB2ydSemXq-|)NbMW%mJ{vVShHT8jh;X>pPxwgS{n|K*3H(Ca6lJp5 zl%Lw}?yeZpHvonPoB7VaA63Ug@72tSfq7wARggNeef?`G-P=VNuBkOWFC7q-*N&YM zqEB9mS*AMIIc=RaSw<}ZXqlFDz%6S3TPxCeSsRfxi-fH) zUS9+jQzT3vq-D0Erz60WP{OT}f2|Z2_AdVqbq#x?Pr?lQ3gf#2Gyd67Y*;%UMj#^` zv||uBz|;;W)=X!_)DcK?ELN;KuUa05+8`$vE)F%;l(F4OJ|1^PGNEfy=ORZ>29JG>AJWvCA-@cu*IC| z6Dhk0&WLdSAhH-NRR{ZF}VWTU^C(d_8Rj{gCWZaQG*Uk%yb%XxSYCKJ+ zy_#YSbzA;t-?0uCy{ zz}hs-LhD4mt#0j)s^ckk^e1j|)dra(*Ghvdq~gK1g;CCD(oJuBL9cYy<}V%Y^Yh;& zI^9Mjv_{>$XntvEv;FIW`{jlzzCf9?^uSps7#wORECRBL%q>s2JB~I`(8(k&i7fG)ezery}BHNTl zKz}))?O~UJNiCyHFV@mWm2SE2p07oKu`%hl^mNzWcaWbM$EyUOni76+&h3-7BX%fd z7|pq*AZoCUk7@<(__LfX0yINl4NA2AY>0ktJGmej#vrCifHc)kSyiSg+!A;e)h-Rv z5fZveRYX-|wVrZEAga5qr?v_`c6E1UQ9bE4wvH0k)JT(x+L_duc*bh=%> zbhCX|Ri-g@vM(9B?$mA7C5h`e*4t@kVog1+OCJsrs|mu4C5XgTSWOcnS7!;) zu4s@Y)=gsxd|Ud;-!{uE>mLXPQc}%PgpXMEu$oOQCF>g{uP=4E*2o&{P4ka+%|<0~ zV#zW)d*_{QzSR(sEJS(70R_YzkD95srZJ53Pq&q*UAuLb-Fac#N+I*hV$SW$x|y7Y zYVDb|J}~#a-uJtD2+??wr%Gjt6I=6ab&H||?cB_M-VN(~BK2ZTCUFGyhr?cOibqDCL=JgMprFD$*=yovyCqvxbk+8kFc$?F| z7tu~WmAHq#yToeq2H4O-sL&vW0^-cEjX7)PyIQ>K<7(4WkgVG?G*Yfag5w)f3D)67 z(Gi;U_3pZ-X=|=T^4Uf%){09B?0 z^FF!u@ShH+LzK_L4Q)lSxpC;{{&D*Br4>YYxLa1eN~J?8{#o*4P}J)F(S5kq&Z^F- zSbXHy1;gm2c}9`BoP@gn*4LF@StmXWd9jd}_sD=yE8+h#%L02L*C|B^n*Q+-X|iq4 z#Ltpk<4@aj{EO?n`Yrg{ZPzZ;W42ioZn+K@^yNUWbF2K_xAcTPRZ-j!`<9cCY7Iq~$-hr)qkN(-7j3K&vK6F?4=YxuU%; ztFfBp%a`!GSiihz?OQ*N?YlHAU?`;PWoJg*+CPnlL7n@yqYoD^TKWBZs&sL~4{C%W zj~WZc%ms!GbC78PXn~>!8?K_f?-2I;)6ewxu7*mlmWHs*%tcGZhRt;XwNBmswr{ng z8E!f28rDFx(~=`$QU+lk<=Y}BFU}ze$%D`xD>k3^b?iSL`Kc0`*1b}_ta0G(Y-#P= zY3ZhGY;_j#wK@=CNk(5*UkprK4&>)zD8tUYX; z(3kMUlF2ho8RRM{J$H4y3M3d91z`oZv`!&g;=rkMkn0Q9mBvmcW*tL}Vhk8Bf>N(T zGa~C0C_Z?c{ue$PFh#L_P3_7?lv}SzW-%o{W{~1- z)qQcQEf7zoZ26sf9KpBd8?&3SuMbXiG1;toPx@KAI!m0?-6FLbPxECF#%QsUC69x8 z$7t;zYFImQC%Wbi!E8s>GreJ7}ev@_)NpjLL66 z`A7SlG~x?BY&3uDJsHNvxFk~Di!`L19cdrw*|S8%k%7tvSNqi_QWYg+I+d8*voe8M z?$8ZfyEBvZ6st&AnY{CPq`C3^Z=~&yfLbhLBF1_6h*>JCktVZg<;uN;l95yT?`Xff zd-jl3jaiab0uX={`P!0s2XJUYqS&lFDXiUx`w8lPdDekS+HFf2a!J)FBRuTcCW>&F zfp73mbEj0*;ui6y|6%F4q@10R%x(~p--}h->2qF;onaHPMf^M3NVlY~GqS{2ALqW+4dY58y6cFoHY+gm(K^|T!(a2P9+Q7;g@Sm5K`kLb z!nG7=jY9+!XfxkE=DaRbOo|zp(>>~IH;s`onp%TmhZ@woh7K_YBA3i?3J<{Oh&>8M zlqpFBDH>HMsUiqOAgTT;3>bsV=_U3_NfNfyK)8Y+!=)TVQ8FP3*&ahl!jaM;mFbY@ z3Mfoy(wf3(GZN^d07VXfrO=gu{_+R!`26}trElQ5nf1;5(;SJ;=U%531XCzw5}QSzrWa@z!!dO@nr!`f9y;TmwuHK zkO=MEU_Z8RC0xwZDSL;`ls#-@lZJa(LXn0=j;?@|MXmVTyS9bG?ZV>f0*ELuL4;Wx zu${PAIW)?$1g2%KA%-QFOe|K`E}3U;VXz#nms&6sakOL#IY6csEm0=3jN>_KtxhE{ zu))i?9Ale?ZPZnUu+Q~$tj#J)3n`&y+!nDO-6F9~PXE-R_36NCxsF8I zXs+vCxid|19Y*AwYrAD=lI)gKR={8P+op3|0Wyat$7{}?Va2^dP38+CL?Ecid7DH| zTGWRTYNMFuE{KiCMaOS%SnFk)D4`L{S4_Lk#j{0NotLVOszU+iHB6k+VoH}@Vx}>) zHWR0#Jp||7cf@()z(#1yOqi^<)=w^7hjg}I*}rRnB1!=pMj7d5RS}h`*SE(tzNd%M zdPjL_XD?O4ni9QtlnN;mD@?#f8R7r4P-Im)oNC)AutlIQ@wi6jn-ZAdpH>bvBFZe>(!#lYFz=GB+j8x~+PxdNl!B0LM z-JUeIat8QZk`_Z7onIK%sn?v#jLq6_ZX|&PpT)}hB;wUB5f%s3pYqH5Q|R>G>0z|? zQ!0EBqoY>H8~vT%OzI8c;h)asI81ArbCdehOkqZrjtv-*oW_Jj1lI3f7hPQu3~-VK z^jBJKa+*b>HoT9TRT*{0JvuPXP30-u*z~N1#;(Y_nbt0{yK=(hh6LlqHFA7*xzn8a zwRR{TH7TJM(fG{Oe^*zZdHZ0e%-3B^WUU4wA{95CsldjFF^GQ>AYL@TsT8I0HgSyfXh1UaInE4g*Kknwn%sU@jD`XNu;^t#XR{UVJszx?}R))$~A(mUlfK48i*FP4n zHQrI=k2K0E#+a1Ymr`gY0KuDXzUM*#AcvI6N0{3rvgCe`JW&Gh6;}u)<2cEJQS`Bb z4?0SSt4I4?MNe4z`e_?lV36LeWgfT^6k$)PRAV_TgP2COfkJtBo zCNIncvf=I1c2iKxWG2LSa7}j)xj(&m!~MQ?x3LN@(CyQ&D5(wP%u5kkwWta?DP3k+ zTuvZK=3zD~ToQWF*n?g{0e5anq7ZY7X|nK}y>)3sbvX-_?rrKZ2p=mWX&UZT6dc;t zK?u#L*Vm*H&+^?dR0#nj@sk_w>(hxT2E-1k6It=nak43lt)+6nm4-mXSRxO4*u}D4qY~5RCuxe^oz?`GtIT{?=yRp z5P@rR(jZ!DYStQirQvCe%CidErx^uXKrX$0rl(rfOFa>Y12XS9I>AM{v3ZdyO};qO z-97z!!2qF%Db{AL>6`{Yh_8N9rY23|5BP}_cdVZhQ%uPk%*gz}Hv>r)&bGCr*O~g5 zSaF?C2bqzIbD5G>`+JVtYRE!JgUVcnxO#0iD8?@dX-ZOr;nGCToZfBP7k2W~yJ$*c zOo@&{B!ZkA?#(4II7KfKup;&5rx!vVpOSbDxrDBB4q9i); z_?>4vPt`Go4Yblhtf?`rN_y8FX6z;QP%DfQ5e_AUNAG_eFv=onT1dV9Y^wZc50=|k zb8}>i7Gi2h*Q4`I97fkF(n4a-g%35{)h+%r+X`D*`9S95nqikC{%%d&)>ogUm~}{k z3BTWMCs@Fg{%@-^pP#q8t^ICZk)|d-{OF>C%xX%eHVSkql?rVO)G76stz;?{(G<=c z+#1~6jd?-r0F?Alfdqg=6b&ph`BWdoiar1bPJ`w>-f#(~bde?$(BwivrUx>t;H$iN zoP%35rI4(e8ll=klMxIbaw4JT0o3(GmFh9-seM~SSbl$O!&&-!LoogD#fv1%EQB+D_; zQ;dq=`)F!roN;Aq3Dh{rILea~5LqUyluXWSG})%$S38*ld5I~~#U~Mu_;MfHe!KD5 z{LC@=l$nv3DE7W$o>fA%WB)ITx@}4c{oQM8&)3jS(ww$5`SBwZN9Fm;4Q^2Q{KAJ< zwqyRzdE((@_oOk#vY40K-+SFeqfRS7!>>C)?d`y<#yf8;seI7H>U9#{u+(>(-Unsw zL*jXz<@7}mTe8;0sxU#p&1k4mrgvLWCPm>pY|4#%dgAmNzG#(uT-5P)H;NfUZ8!>s zyiL}or8tv`sZ+>v)Mk0H`jRK)1g$ji4gOvWzvuR^Ek@5TRl4-doSZS33N)o(dO%)- z{HkbG|H$rFa46(x1pb!07)U3OVkrv}TBV56Z`-D;#v;r3ZM%4p_NIs%Aat%$V-PP% zG*qAzcq5}grlXgBZ1l=}LQXv|04mf#)jiCxXerB5_x zWz{m5-cZuF#sZ`;HhCPt_Q><0=$X{=x?v6zI2f0p5?OSJO1QBSBB2==DHp1*?PV<) z5TxU~Viy##s%AkzG{ow89GM1kE!PzjDhbA81J9UiJojnm%qZQck$Bb*SGnd?{aLIs zwjTLGxqGsBwBroRL=oJ%+kSfu7-}npsm8T}HIW3*$`z5Q5G-kodjXd-W~%9J5s|H+ zlM?0%OS=~p95)DyD_b&#MWl0DJV&anaq&tW(_fr zdSgm1UH@OvTBJ!l_s8_CZ#nE25X}{dwJtQRMUWm2nKk9rygt3wWg>dAQMWcl#f;%R zF~!DGMB#T7%dOq&8|azhNN)L>t=0tgO2vvE8>#?EjIPDa5^*l|kSU$cWL6aeOtTQi z6&%bVQr=_?W(aBuR-~gdfokS=xivAf1w$2>bwU#YCMHuOHq^NjG_ssBSz*QHy$m|a z6}Bm9JhUYqZ2oX{xKtL$j~y}1scYe|L{SkG5mZl&v?w~7n7rI(9Au*)mO_?l?=$xx z0DeV(YAA{F1n7vLaDe-fI)ZQySQQCMb^$wqKpK*9s!swe0GKVm3}(G*1e;s;?+|3pp-2bzfjtRP6KB7}q} z!lFV5FexHP0z!;{2h0HT5Fc_Ur9t_kh@Y7gqOz!+>Ks&GVybxpbU*<0f`P0r`MGAVB^Iq9=HO`-KDEgas&2l$0cpv=D;;A@-sN_$$x@po99If~y#n#-dfjD!=A#1ky+nI80R5Cd*)q zF}5zib(^;XR4ZZ=ve;oYnb68h7{oNvz0nmcw5>EM)CEAOxgjY*GdHAP?~~&@%ntjT zU^WFAU~bv5fmMcEZ00=C6i$@mP46Ljb*I0pYvUktR{lMu2!tJ+DbsRi~zVY zR(_4$ih|-ue{&*!geNay2KxUuaS@`p?JN|}TzNey}Y9~z7 z;&2Nik_&{zh!7+jpwnkZN(;MUHMKOc?0X9BaT@X0Ypk%q71HUoVrWE)L~nC?cS~gs zFG-VY3ns7Rb((>59Xfhm*up%VGN({3B+Z*QbX%$#vrRXPSy^_~t5~;FK&eb*=Mb+v zT8byKY&U7GSs+H;I=n>gXAvwhF$yxfWz< z5fX;0k-3{-h@IxqPMr&6oe?Rlpr~6W*oM+>yd6MGuXML@KQ^_;F!D?CRD zZJN89^p3#MO1k9=UKv0}22sR}#-*bITZ}E?v4gAPg$Xwo5?4Gica0@5(cC1W~LY2XY1f*nZ_(3lgCy5gY3s{nvEaFom z7H=2Ojq409$y4pT<$b+UF-75HN{*!#ldDXQ1`r@+Qwr_N3hVUUU3TmWEh>qQ z5=6i-gdu>6a%w4B^%hH~OU>7rPA0PlC8ZH$#unyh3o#+CZ4oO;+|5&*O|3YDs12Cb zwM7h3AyH*zh#XE`Lt-x{9hc`Y$AK{jXQ64*sZhX%vSNs16t$);yGEKWO-A=Sv$@s` z);D(Wc1)W?v_i7;Z%OvF$eZd_t5Y*GU9%(3Op}-cvK!oTOWeg}nTp-C?&FLL5W;CQ zIb_f-W~!xUU0Kdx@_`-m`RaCP0%Ne8x)_O(q6Yw#2ub%t>Rm9X13< zUDnMV%%61yrZX1_Lnl0lh#agU$cC^I;K6JOT(efsS<=Wu7$I%eanW>@=A@+;7hA78 zVv(ED!mR>WT`k6$wOluD^7i%nW#&d}iIcp`!eMBd@y43WkTsmsm=fb|jguKujv;ig zcW{RWl;b42)sswRLUPdsmEe~HW4g5xFnO(XWH4Ojk~Mwm3TGr@2`H{-o!td<0Ib4k zT?#V#INgqrv#qB%%00~oWTs~kCKH@C(#7MA)RT%>94XU8ha5txG6N1G<2u=5qm>1Z z)t#NiD9KywPn@?Qh!WEw+XW^7Eg0?9a96CiU{yNZ-7JP&o1>FlZOnMs3)MnaA-3f! zmL^97Foc4k7XE3Ku9>MsoV8raICjP@yNNpIr6lcH#7$Z__T8kdzUMj#t7-%5o3l%m zinPSQh7vXP+7UcVmB!l#d4R?awya>LpsGcgxeQ%IK}oEx8BI*w!FG+MOXi$^89Tl1 zm@`sc{22{HK>$@$26&5Uz^HCet_kejL-3c59*da2oWG~ zjza?g1orW{kjl18`>*=9>|CF>=!`PtKT-(sY z7^>9G7iCZ-!sJB~^M#j9b2*t1pe=*i!rM1W$yajk;QDK4KIzn0YBg6BpGDMS6MFJ= z7sbWCnya|I+npeHUR~~E46epNgdr;LX?AYzu(?E#8Uk#wTyBw%eCW?{_5W^rSW@8d&NX9U;6m79yl?sbMUDA?nOx#GVNsR@;Ajt?xywIA& zRu)#;SE2lFQq1X?BM>E#wl>^)s-wJ->;Em^UXk@z3aSMga(cR1{F0PnZ)d7&d~~Fc zTh_(99~Ks>qSC2bV5eHtGCixco7+Q+-0R#R2R72iv1MAzXol9P3JMlz<8Lko%{RI( zzN%m7f!*sggKWND94!~?9WwLGzw-F7s!3%T$*^D|ko>MuLF z*RbfMMg`lOlnjy_q0Q$s)ik~(V|AHpnGNe_oQdHl3M8|wFnos_5m6I`|7`|H9Z(T9w z3m^nA`Nk~b&B1^%JiVz?Hf$qL0V`0+ES$E+SnzL@mX)bF?RRYcMB*ys@^en@QY4!a zVG{vVcbkj0!VYcN6prj&?uMv5vRzYF>&>y7EKr8-M1~}xsL>VgqmaJ7u@4j(dDBk}yWq|}hN;QUG76sBtlthQ*c*kN{Y|?6)hY=+e7hD{>x@FYLixC<2Cg;wE zCM;lT18R_kWl2WhtC-R*M8&fX*_%wuG|IKjmKd0jsVoH$t#o4=QKt&rY7R9V8JUu) zmamRKhc4*t^+r+0n;v9ogI3TmJ}5TGlkSUSG(0q&%*ge-#*?{D?zz)t>8Td8 zicOT35(LkAoJJkJ+HM-r_iUZLXvOa9cxOu?8rnuJDG35uY~I?Cb+~sp_T?o#T0-Y~ z5v-+X=?EOGEmcD|X3jXBoHp=n64g*%a4S%aiYcn7QR8r9afKnsqG+)O3z4gtF^1^O zw(RArdD&8=iLP)?X~S*v7j+ee*sr~}oj19z#yp+O-N)Nw>DiPpb^`X3&?Me=bEkPP zLkdTcJz7~9AmeDaRfCDAuW(K2b}Fi-*PYFFk6%|oHzhhtlFPKZ&>a(@u~n-I(v>}# zAh@LNEvb)C$sKB(g*SRU)O9ysDW*21?b7QRS(Uzanx=&15|+mn$|M_diSxV3nia0L zM8^uY#-wI8sHn_)#~8Q+vJqvn!Hl303j*3fXk{5JUapeaY$2}MwPwN#?cOO$O%>_L zdQj0#H(1-)_U)(sHOmD(7q7VJF?!KEV_n`|kk%JUHLn(doO)E9D}2n-7)j!Rfhwrr z1duWc-8M&Hnl9H8K&xS~S20JM0Mk=8!y1PzATLZDRxPP$Ryj{yAltg*G>mF0NCA`# zv2NO`DwU>z0;a)zSwuS|N%LfEEVyccnt@?~kTO`h*5A_D;wCDT5;emVF)|tb4m4Ou z3KUvWp|n(Z)=hHQE{>v$cg=g+i3Fb0n9Ty$wr>XQy6maeIQd@X*R0gy~rTiq>y*mQ(fu$HKRdo)vK+Rashv84qJhMINgpE|iX zAWvUMXHFqZFDXO0>sE=|yS=1)C>h9Pd#D`}g^V3~ajUrNM>=pSnxom(Xt!Q6#p>yI zj@~QRiK{xkGr`Jx%+sjN28_|_kl|U~;&->hpuMEyob}nc?ip4b zqgpb#Jsq~}=5#Mw^xdYZDGVVUA_WAbB20>I+PS9)Om8E$&L28VdnqF#1XW9Dx0-Zu zzs4;`hu&(G_4ea><}^3@EnA{AmYg`pTq>=x7OJT>?#1M=eLqG424;-cVt0#O)RQsb zfwk`Bv?I*9plgvbFofY(22rVxiE0X#s?#)*A`}yi3f8aZB2O5xg^;f|X4*swkkXpt z(Wh~j3R!MC3YI(u$<{=v25|wJ!W*XauJm+u+0ROCmevrBHeBW@G{_Kus?g@>=DGQ; zU1kX}YUfi#fYU0rX5ffTH5O@+N02GmThN`IR&95=%T^W)FNG+QWngFz* BrKJD> literal 0 HcmV?d00001 diff --git a/share/doc/networkx-1.11/examples/drawing/circular_tree.py b/share/doc/networkx-1.11/examples/drawing/circular_tree.py new file mode 100644 index 000000000..77216bd76 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/circular_tree.py @@ -0,0 +1,22 @@ +import networkx as nx +import matplotlib.pyplot as plt + +try: + import pygraphviz + from networkx.drawing.nx_agraph import graphviz_layout +except ImportError: + try: + import pydotplus + from networkx.drawing.nx_pydot import graphviz_layout + except ImportError: + raise ImportError("This example needs Graphviz and either " + "PyGraphviz or PyDotPlus") + +G = nx.balanced_tree(3, 5) +pos = graphviz_layout(G, prog='twopi', args='') +plt.figure(figsize=(8, 8)) +nx.draw(G, pos, node_size=20, alpha=0.5, node_color="blue", with_labels=False) +plt.axis('equal') +plt.savefig('circular_tree.png') +plt.show() + diff --git a/share/doc/networkx-1.11/examples/drawing/degree_histogram.py b/share/doc/networkx-1.11/examples/drawing/degree_histogram.py new file mode 100644 index 000000000..a6dd8742c --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/degree_histogram.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +""" +Random graph from given degree sequence. +Draw degree rank plot and graph with matplotlib. +""" +# Author: Aric Hagberg +import matplotlib.pyplot as plt +import networkx as nx + +G = nx.gnp_random_graph(100,0.02) + +degree_sequence=sorted(nx.degree(G).values(),reverse=True) # degree sequence +#print "Degree sequence", degree_sequence +dmax=max(degree_sequence) + +plt.loglog(degree_sequence,'b-',marker='o') +plt.title("Degree rank plot") +plt.ylabel("degree") +plt.xlabel("rank") + +# draw graph in inset +plt.axes([0.45,0.45,0.45,0.45]) +Gcc=sorted(nx.connected_component_subgraphs(G), key = len, reverse=True)[0] +pos=nx.spring_layout(Gcc) +plt.axis('off') +nx.draw_networkx_nodes(Gcc,pos,node_size=20) +nx.draw_networkx_edges(Gcc,pos,alpha=0.4) + +plt.savefig("degree_histogram.png") +plt.show() + diff --git a/share/doc/networkx-1.11/examples/drawing/edge_colormap.py b/share/doc/networkx-1.11/examples/drawing/edge_colormap.py new file mode 100644 index 000000000..a2aa7a3a4 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/edge_colormap.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +""" +Draw a graph with matplotlib, color edges. +You must have matplotlib>=87.7 for this to work. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) +try: + import matplotlib.pyplot as plt +except: + raise + +import networkx as nx + +G=nx.star_graph(20) +pos=nx.spring_layout(G) +colors=range(20) +nx.draw(G,pos,node_color='#A0CBE2',edge_color=colors,width=4,edge_cmap=plt.cm.Blues,with_labels=False) +plt.savefig("edge_colormap.png") # save as png +plt.show() # display diff --git a/share/doc/networkx-1.11/examples/drawing/ego_graph.py b/share/doc/networkx-1.11/examples/drawing/ego_graph.py new file mode 100644 index 000000000..e1092738a --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/ego_graph.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Example using the NetworkX ego_graph() function to return the main egonet of +the largest hub in a Barabási-Albert network. +""" +# Author: Drew Conway (drew.conway@nyu.edu) + +from operator import itemgetter +import networkx as nx +import matplotlib.pyplot as plt + + +if __name__ == '__main__': + # Create a BA model graph + n=1000 + m=2 + G=nx.generators.barabasi_albert_graph(n,m) + # find node with largest degree + node_and_degree=G.degree() + (largest_hub,degree)=sorted(node_and_degree.items(),key=itemgetter(1))[-1] + # Create ego graph of main hub + hub_ego=nx.ego_graph(G,largest_hub) + # Draw graph + pos=nx.spring_layout(hub_ego) + nx.draw(hub_ego,pos,node_color='b',node_size=50,with_labels=False) + # Draw ego as large and red + nx.draw_networkx_nodes(hub_ego,pos,nodelist=[largest_hub],node_size=300,node_color='r') + plt.savefig('ego_graph.png') + plt.show() diff --git a/share/doc/networkx-1.11/examples/drawing/four_grids.py b/share/doc/networkx-1.11/examples/drawing/four_grids.py new file mode 100644 index 000000000..112019170 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/four_grids.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +""" +Draw a graph with matplotlib. +You must have matplotlib for this to work. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2004-2016 +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +try: + import matplotlib.pyplot as plt +except: + raise + +import networkx as nx + +G=nx.grid_2d_graph(4,4) #4x4 grid + +pos=nx.spring_layout(G,iterations=100) + +plt.subplot(221) +nx.draw(G,pos,font_size=8) + +plt.subplot(222) +nx.draw(G,pos,node_color='k',node_size=0,with_labels=False) + +plt.subplot(223) +nx.draw(G,pos,node_color='g',node_size=250,with_labels=False,width=6) + +plt.subplot(224) +H=G.to_directed() +nx.draw(H,pos,node_color='b',node_size=20,with_labels=False) + +plt.savefig("four_grids.png") +plt.show() diff --git a/share/doc/networkx-1.11/examples/drawing/giant_component.py b/share/doc/networkx-1.11/examples/drawing/giant_component.py new file mode 100644 index 000000000..9ec3266c1 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/giant_component.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +""" +This example illustrates the sudden appearance of a +giant connected component in a binomial random graph. + +Requires pygraphviz and matplotlib to draw. + +""" +# Copyright (C) 2006-2016 +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +try: + import matplotlib.pyplot as plt +except: + raise + +import networkx as nx +import math + +try: + import pygraphviz + from networkx.drawing.nx_agraph import graphviz_layout + layout = graphviz_layout +except ImportError: + try: + import pydotplus + from networkx.drawing.nx_pydot import graphviz_layout + layout = graphviz_layout + except ImportError: + print("PyGraphviz and PyDotPlus not found;\n" + "drawing with spring layout;\n" + "will be slow.") + layout = nx.spring_layout + + +n=150 # 150 nodes +# p value at which giant component (of size log(n) nodes) is expected +p_giant=1.0/(n-1) +# p value at which graph is expected to become completely connected +p_conn=math.log(n)/float(n) + +# the following range of p values should be close to the threshold +pvals=[0.003, 0.006, 0.008, 0.015] + +region=220 # for pylab 2x2 subplot layout +plt.subplots_adjust(left=0,right=1,bottom=0,top=0.95,wspace=0.01,hspace=0.01) +for p in pvals: + G=nx.binomial_graph(n,p) + pos=layout(G) + region+=1 + plt.subplot(region) + plt.title("p = %6.3f"%(p)) + nx.draw(G,pos, + with_labels=False, + node_size=10 + ) + # identify largest connected component + Gcc=sorted(nx.connected_component_subgraphs(G), key = len, reverse=True) + G0=Gcc[0] + nx.draw_networkx_edges(G0,pos, + with_labels=False, + edge_color='r', + width=6.0 + ) + # show other connected components + for Gi in Gcc[1:]: + if len(Gi)>1: + nx.draw_networkx_edges(Gi,pos, + with_labels=False, + edge_color='r', + alpha=0.3, + width=5.0 + ) +plt.savefig("giant_component.png") +plt.show() # display diff --git a/share/doc/networkx-1.11/examples/drawing/house_with_colors.py b/share/doc/networkx-1.11/examples/drawing/house_with_colors.py new file mode 100644 index 000000000..78aa15219 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/house_with_colors.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +""" +Draw a graph with matplotlib. +You must have matplotlib for this to work. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) +try: + import matplotlib.pyplot as plt +except: + raise + +import networkx as nx + +G=nx.house_graph() +# explicitly set positions +pos={0:(0,0), + 1:(1,0), + 2:(0,1), + 3:(1,1), + 4:(0.5,2.0)} + +nx.draw_networkx_nodes(G,pos,node_size=2000,nodelist=[4]) +nx.draw_networkx_nodes(G,pos,node_size=3000,nodelist=[0,1,2,3],node_color='b') +nx.draw_networkx_edges(G,pos,alpha=0.5,width=6) +plt.axis('off') +plt.savefig("house_with_colors.png") # save as png +plt.show() # display diff --git a/share/doc/networkx-1.11/examples/drawing/knuth_miles.py b/share/doc/networkx-1.11/examples/drawing/knuth_miles.py new file mode 100644 index 000000000..0ba9fd767 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/knuth_miles.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +""" +An example using networkx.Graph(). + +miles_graph() returns an undirected graph over the 128 US cities from +the datafile miles_dat.txt. The cities each have location and population +data. The edges are labeled with the distance betwen the two cities. + +This example is described in Section 1.1 in Knuth's book [1,2]. + +References. +----------- + +[1] Donald E. Knuth, + "The Stanford GraphBase: A Platform for Combinatorial Computing", + ACM Press, New York, 1993. +[2] http://www-cs-faculty.stanford.edu/~knuth/sgb.html + + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx + + +def miles_graph(): + """ Return the cites example graph in miles_dat.txt + from the Stanford GraphBase. + """ + # open file miles_dat.txt.gz (or miles_dat.txt) + import gzip + fh = gzip.open('knuth_miles.txt.gz','r') + + G=nx.Graph() + G.position={} + G.population={} + + cities=[] + for line in fh.readlines(): + line = line.decode() + if line.startswith("*"): # skip comments + continue + + numfind=re.compile("^\d+") + + if numfind.match(line): # this line is distances + dist=line.split() + for d in dist: + G.add_edge(city,cities[i],weight=int(d)) + i=i+1 + else: # this line is a city, position, population + i=1 + (city,coordpop)=line.split("[") + cities.insert(0,city) + (coord,pop)=coordpop.split("]") + (y,x)=coord.split(",") + + G.add_node(city) + # assign position - flip x axis for matplotlib, shift origin + G.position[city]=(-int(x)+7500,int(y)-3000) + G.population[city]=float(pop)/1000.0 + return G + +if __name__ == '__main__': + import networkx as nx + import re + import sys + + G=miles_graph() + + print("Loaded miles_dat.txt containing 128 cities.") + print("digraph has %d nodes with %d edges"\ + %(nx.number_of_nodes(G),nx.number_of_edges(G))) + + + # make new graph of cites, edge if less then 300 miles between them + H=nx.Graph() + for v in G: + H.add_node(v) + for (u,v,d) in G.edges(data=True): + if d['weight'] < 300: + H.add_edge(u,v) + + # draw with matplotlib/pylab + + try: + import matplotlib.pyplot as plt + plt.figure(figsize=(8,8)) + # with nodes colored by degree sized by population + node_color=[float(H.degree(v)) for v in H] + nx.draw(H,G.position, + node_size=[G.population[v] for v in H], + node_color=node_color, + with_labels=False) + + # scale the axes equally + plt.xlim(-5000,500) + plt.ylim(-2000,3500) + + plt.savefig("knuth_miles.png") + except: + pass + + + diff --git a/share/doc/networkx-1.11/examples/drawing/knuth_miles.txt.gz b/share/doc/networkx-1.11/examples/drawing/knuth_miles.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..62b7f95fb8aebea1f93b55924697e2a1958d3b13 GIT binary patch literal 20317 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 A*#H0l literal 0 HcmV?d00001 diff --git a/share/doc/networkx-1.11/examples/drawing/labels_and_colors.py b/share/doc/networkx-1.11/examples/drawing/labels_and_colors.py new file mode 100644 index 000000000..cf06a95a7 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/labels_and_colors.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +""" +Draw a graph with matplotlib, color by degree. + +You must have matplotlib for this to work. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) +import matplotlib.pyplot as plt + +import networkx as nx + +G=nx.cubical_graph() +pos=nx.spring_layout(G) # positions for all nodes + +# nodes +nx.draw_networkx_nodes(G,pos, + nodelist=[0,1,2,3], + node_color='r', + node_size=500, + alpha=0.8) +nx.draw_networkx_nodes(G,pos, + nodelist=[4,5,6,7], + node_color='b', + node_size=500, + alpha=0.8) + +# edges +nx.draw_networkx_edges(G,pos,width=1.0,alpha=0.5) +nx.draw_networkx_edges(G,pos, + edgelist=[(0,1),(1,2),(2,3),(3,0)], + width=8,alpha=0.5,edge_color='r') +nx.draw_networkx_edges(G,pos, + edgelist=[(4,5),(5,6),(6,7),(7,4)], + width=8,alpha=0.5,edge_color='b') + + +# some math labels +labels={} +labels[0]=r'$a$' +labels[1]=r'$b$' +labels[2]=r'$c$' +labels[3]=r'$d$' +labels[4]=r'$\alpha$' +labels[5]=r'$\beta$' +labels[6]=r'$\gamma$' +labels[7]=r'$\delta$' +nx.draw_networkx_labels(G,pos,labels,font_size=16) + +plt.axis('off') +plt.savefig("labels_and_colors.png") # save as png +plt.show() # display diff --git a/share/doc/networkx-1.11/examples/drawing/lanl_routes.edgelist b/share/doc/networkx-1.11/examples/drawing/lanl_routes.edgelist new file mode 100644 index 000000000..db831813c --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/lanl_routes.edgelist @@ -0,0 +1,1363 @@ +1 0 9 +2 3 173 +3 4 167 +4 5 165 +5 102 96 +5 6 96.43 +6 7 96.62 +7 8 86 +8 9 87 +9 10 82 +10 11 79 +11 12 74 +12 13 41 +13 1 3 +14 15 187.46 +15 16 187 +16 17 187.04 +17 18 186.62 +18 19 185.88 +19 20 185.4 +20 21 184.02 +21 22 184.38 +22 23 183.97 +23 24 93.01 +24 25 88.59 +25 26 86.26 +26 27 88.04 +27 28 77.12 +28 29 74 +29 119 70.1 +29 30 72 +30 31 73 +31 32 89.41 +32 13 6.63 +33 34 212.25 +34 35 211.46 +35 36 209.04 +36 37 211.57 +37 38 206.57 +38 39 105.04 +39 40 77.21 +40 41 86.5 +41 42 0 +42 1 16 +43 44 112.67 +44 45 111.94 +45 46 111.77 +46 47 111.83 +47 48 111.27 +48 49 111.76 +49 50 109.32 +50 51 110.67 +51 52 80.63 +52 53 80.78 +53 41 30 +54 55 164.62 +55 56 164.13 +56 57 164.24 +57 58 156.43 +58 59 88.92 +59 60 88.6 +60 61 86.53 +61 62 85.96 +62 10 86.8 +63 64 221.31 +64 65 220.88 +65 66 220.84 +66 67 220.27 +67 68 215.19 +68 69 209 +69 70 213.81 +70 71 208.04 +71 72 196.45 +72 73 197.05 +73 74 163.85 +74 75 186.4 +75 76 180.35 +76 77 103.92 +77 78 98.6 +78 79 98.83 +79 80 96.09 +80 10 309 +81 82 174.3 +82 83 173.57 +83 84 173.9 +84 85 173.66 +85 86 173.75 +86 87 92.62 +87 88 73.93 +88 32 91.44 +89 90 158 +90 91 155 +91 92 158 +91 97 157.76 +92 93 86 +93 94 325.32 +94 32 5.53000000000003 +95 96 158.17 +96 91 157.6 +97 98 84.74 +98 94 84.87 +99 100 282 +100 101 279 +101 5 167 +102 7 87 +103 104 201.16 +104 105 200.5 +105 106 200.5 +106 39 121.02 +107 108 148.27 +108 109 147.59 +109 110 147.87 +110 111 147.88 +111 112 147.82 +112 113 140.19 +113 114 147.54 +114 115 64.02 +115 116 67.75 +116 117 70.55 +117 118 98.3 +118 119 80.4 +119 31 81.28 +120 121 161.26 +121 122 160.84 +122 123 160.86 +123 58 160.84 +124 125 238.34 +125 126 237.79 +126 127 237.08 +127 128 234.34 +128 129 236.72 +129 130 237.18 +130 131 119.25 +131 132 111.15 +132 133 30.93 +133 134 50 +134 135 49 +135 42 43 +136 137 148.68 +137 138 147.78 +138 139 147.74 +139 140 148.29 +140 141 136.56 +141 142 145.37 +142 143 144.93 +143 144 141.78 +143 160 144.92 +144 145 141.65 +145 146 72.32 +146 147 68.81 +147 148 73.15 +148 149 93.13 +149 150 82.38 +150 151 86.03 +151 152 73.3 +152 153 80.2 +153 154 83.08 +154 32 0 +155 156 152.24 +156 157 151.47 +157 158 150.17 +158 159 151.59 +159 140 145.38 +160 145 143.86 +161 162 197.3 +162 163 196.83 +163 164 196.73 +164 165 196.8 +165 166 131.22 +165 383 128.8 +166 167 36 +167 168 44.08 +168 42 11.49 +169 170 178 +170 171 174 +171 172 166 +172 5 166 +173 174 213.7 +174 175 213.01 +175 176 213.03 +176 177 213.38 +177 178 202.81 +178 179 204.73 +179 180 203 +180 38 202.46 +181 182 44 +182 183 41 +183 184 43 +184 185 44 +185 186 42 +186 187 33 +187 188 32 +188 189 39.44 +189 42 36.28 +190 191 258.85 +191 192 257.61 +192 193 258.1 +193 194 87.26 +194 195 88.26 +195 196 85.82 +196 197 18.93 +197 198 32 +198 188 33.68 +199 200 114 +200 201 110 +201 202 114 +202 203 112 +203 204 29 +204 205 36 +205 206 37 +206 42 27 +207 208 562.86 +208 209 561.89 +209 210 561.94 +210 211 9.50999999999999 +211 212 14.12 +212 213 6.81000000000006 +213 214 1.72000000000003 +214 215 11.83 +215 117 85.94 +216 217 114.12 +217 218 112.57 +218 219 112.04 +219 220 112.01 +220 188 88.74 +221 222 211 +222 223 208 +223 224 211 +224 225 103 +225 226 104.75 +226 227 103 +226 687 105 +227 98 73 +228 229 67.83 +229 230 66.68 +230 231 67.39 +231 232 67.36 +232 233 66.39 +233 234 32.11 +234 188 32.19 +235 236 194 +236 237 191 +237 172 166 +238 239 191.94 +239 240 190.85 +240 241 191.48 +241 242 190.73 +242 243 183.14 +243 244 182.32 +244 245 179.92 +245 246 182.17 +246 97 173.79 +247 248 195.23 +248 249 194.78 +249 250 179.06 +250 251 179.01 +251 252 168.31 +252 57 165.26 +253 254 92.14 +254 255 91.07 +255 256 90.41 +256 257 90.85 +257 258 46.52 +258 259 57.31 +259 260 4.04000000000001 +260 261 19.22 +261 262 8.82000000000001 +262 263 79 +263 41 78 +264 265 102.69 +265 266 102.24 +266 267 101.92 +267 1355 116 +267 268 101.64 +268 269 0 +269 270 80.77 +270 271 89.27 +271 272 115 +272 273 116 +273 274 115 +274 133 50 +275 276 58.9 +276 277 58.19 +277 278 0 +278 279 58.13 +279 280 58.21 +280 281 58.04 +281 282 58.12 +282 283 31.7 +283 284 26.07 +284 285 38.42 +284 574 32.12 +285 286 196 +286 42 127 +287 288 186.74 +288 289 185.88 +289 290 183.24 +290 291 184.06 +291 292 181.22 +292 293 0 +293 294 171.29 +294 295 102.04 +295 296 68.99 +296 30 68.93 +297 298 103.31 +298 299 102.8 +299 300 102.75 +300 301 102.77 +301 302 102.55 +302 270 101.64 +303 304 555.45 +305 306 76.2 +306 307 75.65 +307 308 75.66 +308 309 75.9 +309 310 75.7 +310 311 69.12 +311 312 68.99 +312 32 0 +313 314 170 +314 315 167 +315 316 169 +316 317 169 +317 318 169 +318 319 167 +319 320 167 +320 321 27 +321 198 16 +322 323 178 +323 324 175 +324 325 177 +325 326 125 +326 327 117 +327 328 34 +328 329 34 +329 330 34 +330 331 0 +331 198 33 +332 333 85.6 +333 334 84.88 +334 335 84.69 +335 336 77.26 +336 337 80.75 +337 53 79.65 +338 339 104.12 +339 340 103.35 +340 341 103.55 +341 342 61.24 +342 343 90.09 +343 344 92.58 +344 345 92.92 +345 346 86 +346 9 86 +347 348 106.96 +348 349 106.37 +349 350 75.33 +350 351 90.59 +351 61 66 +352 353 76.11 +353 354 75.34 +354 355 75.16 +355 356 74.84 +356 357 61.66 +357 358 103.06 +358 359 34 +359 168 34 +360 361 122.16 +361 362 121.69 +362 166 121.31 +363 364 63.84 +364 365 63.35 +365 366 63.5 +366 367 48.79 +367 368 47.81 +368 369 38.89 +369 370 38.77 +370 371 37.31 +371 358 36.43 +372 373 210 +373 374 206 +374 375 187 +375 376 343 +376 377 193 +377 378 33 +378 379 86 +378 866 33.57 +378 958 33 +379 285 196 +380 381 51.17 +381 382 50.66 +382 383 27.75 +383 359 32 +384 385 44.03 +385 386 43.56 +386 387 43.19 +387 358 40.03 +388 389 79.01 +389 390 0 +390 358 78.04 +391 392 186.71 +392 393 185.92 +393 394 184.8 +394 395 171.14 +395 396 176.31 +396 53 82 +397 398 133.54 +398 399 133.05 +399 400 133.09 +400 383 132.6 +401 402 101.49 +402 403 101 +403 358 100.55 +404 405 199.34 +405 406 198.85 +406 407 198.94 +407 408 199.01 +408 409 198.79 +409 410 53.72 +410 411 33.45 +411 412 38.86 +412 413 36.46 +413 414 38.68 +414 205 31.92 +415 416 35.64 +416 417 34.93 +417 418 34.91 +418 414 34.93 +419 420 119.9 +420 421 119.37 +421 422 119.02 +422 423 118.89 +423 215 111.28 +424 425 57.09 +425 426 56.3 +426 427 54.33 +427 428 55.68 +428 429 55.89 +429 430 52.57 +430 283 39.54 +431 432 91.65 +432 433 90.74 +433 434 91.05 +434 435 90.27 +435 436 87.89 +436 437 83.17 +437 438 76.52 +438 439 67.26 +440 441 32.05 +441 42 32.57 +442 443 103.73 +443 444 100.75 +444 357 103.22 +445 446 226.87 +446 447 226.41 +447 448 226.52 +448 166 225.66 +449 450 107.54 +450 451 107.08 +451 383 106.74 +452 453 190.21 +453 454 186.79 +454 455 188.77 +455 456 188.36 +456 457 41.12 +457 458 39.38 +458 459 39.48 +459 460 37.96 +460 286 32 +461 462 32.81 +462 359 32.31 +463 464 106.75 +464 465 106.27 +465 358 105.93 +466 467 34.12 +467 468 15.78 +468 469 33.79 +469 470 33.05 +470 471 53.35 +471 188 17.55 +472 473 90.41 +473 474 89.07 +474 475 90.06 +475 476 89.2 +476 477 88.42 +477 478 85.87 +478 10 0 +479 480 142.27 +480 481 8.90000000000001 +481 482 13.62 +482 483 112.18 +483 29 138.4 +484 485 101.64 +485 486 100.96 +486 487 101.12 +487 488 100.77 +488 489 49.98 +489 117 48.89 +490 491 42.63 +491 492 42.02 +492 493 41.89 +493 494 41.72 +494 460 40.09 +495 496 184.89 +496 497 184.2 +497 498 184.58 +498 499 184.51 +499 500 43.65 +500 501 43.27 +501 502 39.97 +502 286 39.64 +503 504 87.59 +504 505 86.98 +505 506 68.34 +506 507 86.09 +507 79 85.85 +508 509 35.46 +509 510 34.98 +510 511 35.09 +511 512 35.06 +512 203 32.71 +513 514 237.49 +514 515 236.63 +515 516 235.99 +516 517 235.45 +517 119 232.59 +518 519 108.9 +519 520 108.33 +520 521 108.34 +521 522 108.32 +522 523 89.23 +523 524 76.97 +524 525 76.55 +525 32 74.26 +526 527 94.58 +527 528 93.93 +528 529 93.74 +529 530 92.33 +530 531 91.65 +531 532 92.78 +532 533 52.01 +533 119 76 +534 535 278.82 +535 536 277.82 +536 537 277.42 +537 538 277.54 +538 215 269.96 +539 540 61.14 +540 541 59.85 +541 542 60.8 +542 543 60.65 +543 544 56.01 +544 470 33.14 +545 546 111.16 +546 547 109.47 +547 548 110.05 +548 549 108.98 +549 550 108.86 +550 551 47.69 +551 552 47.26 +552 205 55.57 +553 554 85.92 +554 555 83.29 +555 556 85.58 +556 557 85.45 +557 558 79.84 +558 559 76.12 +559 478 67.6 +560 561 315.3 +561 562 314.68 +562 563 314.69 +563 564 314.81 +564 565 291.4 +565 596 287.51 +566 567 281.41 +567 568 117.21 +567 597 134.64 +568 569 121.28 +569 570 138.98 +570 571 130.29 +571 572 85.41 +572 573 85.15 +573 283 35.07 +574 286 31.89 +575 576 107.08 +576 577 106.44 +577 578 96 +578 1347 96 +578 579 106.29 +579 580 106.33 +580 204 21 +581 582 182.8 +582 583 180.62 +583 584 181.1 +584 585 181 +585 586 176.62 +586 587 167.92 +587 588 33.47 +588 589 34.93 +589 580 33.76 +590 591 293.64 +591 592 293.08 +592 593 293.16 +594 595 292.45 +595 564 291.9 +596 566 287.81 +597 569 129.61 +598 599 108.64 +599 600 103.72 +600 203 108.12 +601 602 188.26 +602 603 186.83 +603 101 150.99 +604 605 172.09 +605 606 171.68 +606 607 171.75 +607 57 165.06 +608 609 108.26 +609 610 107.62 +610 611 107.17 +611 612 107.3 +612 613 94.52 +614 615 179.19 +615 616 178.63 +616 617 178.73 +617 618 177.65 +618 619 93.43 +619 312 87.42 +620 621 180.61 +621 622 179.95 +622 623 180.24 +623 624 180.2 +624 625 180.07 +625 626 182.28 +626 627 176.33 +627 628 99.93 +628 629 96.99 +629 630 96.68 +630 396 76.07 +631 632 156.82 +632 633 156.12 +633 634 155.87 +634 635 156 +635 636 155.69 +636 637 155.46 +637 638 169.15 +638 639 168.91 +639 640 168.78 +640 28 47.56 +641 642 199.77 +642 643 196.16 +643 644 199.4 +644 645 197.12 +645 646 174.59 +646 71 197.29 +647 648 234 +648 649 230 +649 650 233 +650 651 102 +651 652 98 +652 197 101 +653 654 187 +654 655 184 +655 101 167 +656 657 203.5 +657 658 202.75 +658 659 202.65 +659 660 202.31 +660 661 202.32 +661 662 202.19 +662 663 181.05 +663 664 93.39 +664 665 86.23 +665 148 93.14 +666 667 204.1 +667 668 203.08 +668 669 203.14 +669 617 177.3 +670 671 115.11 +671 672 114.18 +672 673 114.39 +673 674 110.33 +674 675 107.11 +676 677 262 +677 678 261.21 +678 679 256.73 +679 680 258.46 +680 681 258.25 +681 637 257.7 +682 683 234.24 +683 684 233.35 +684 685 232.7 +685 686 29.28 +686 225 103.48 +687 93 321.3 +688 689 89.06 +689 690 88.36 +691 692 88.72 +692 693 88.5 +693 285 88.12 +694 695 637.37 +695 696 636.56 +696 697 634.76 +697 698 628.88 +698 699 615.38 +699 700 102.91 +700 701 85.77 +701 702 78.1799999999999 +702 703 96.09 +703 165 52.89 +704 705 98.12 +705 706 97.48 +706 707 95.44 +707 708 97.47 +708 709 97.42 +709 167 43.83 +710 711 237.43 +711 712 236.85 +712 713 236.53 +713 714 236.33 +714 715 208.15 +715 716 207.98 +716 38 108.65 +716 717 107.34 +717 39 111.29 +718 719 87 +719 720 82 +720 721 86 +721 722 87 +722 723 86 +723 724 79 +724 725 32 +725 396 83 +726 727 196.18 +727 728 195.25 +728 729 194.91 +729 730 195.83 +730 731 110.77 +731 732 111.26 +732 733 111.4 +733 734 111.31 +734 735 111.15 +735 736 90.31 +736 737 93.77 +737 215 96.41 +738 739 456.99 +739 740 454.87 +740 741 455.13 +741 742 454.46 +742 743 422.76 +743 744 327.98 +744 687 326.23 +745 746 241.98 +746 747 241.31 +747 748 241.24 +748 749 236 +749 750 231.22 +750 751 91.12 +751 752 97.31 +752 753 103.3 +753 754 31.03 +754 198 0 +755 756 219.66 +756 757 218.95 +757 758 219.13 +758 759 219.11 +759 760 218.51 +760 761 218.5 +761 762 200.71 +762 763 199.49 +763 203 105.48 +764 765 186.62 +765 766 185.87 +766 5 127.52 +767 768 88 +768 102 85 +769 770 199.66 +770 771 199.14 +771 772 199.05 +772 773 196.95 +773 774 194.12 +774 775 194 +775 776 190.3 +776 777 190.08 +777 625 187.75 +778 779 203.98 +779 780 203.14 +780 781 202.48 +781 782 182.54 +782 783 177.2 +783 626 175.98 +784 785 230.98 +785 786 230.13 +786 787 229.17 +787 788 228.7 +788 789 222.24 +789 790 208.82 +790 791 96.82 +791 628 96.76 +792 793 107 +793 794 105 +794 795 106 +795 796 107 +796 797 106 +797 798 106 +798 799 91 +799 345 91 +800 801 343 +801 375 340 +802 803 191 +803 804 188 +804 805 190 +805 806 191 +806 807 149 +807 808 149 +808 809 146 +809 810 11 +810 811 13 +811 812 33.77 +812 42 16.29 +813 814 201.7 +814 815 200.68 +815 816 199.52 +816 817 199.61 +817 818 199.6 +818 819 45.02 +819 820 44.94 +820 821 26.66 +821 285 43.1 +822 823 156.08 +823 824 131.23 +824 825 149.04 +825 826 151.23 +826 827 142.62 +827 828 111.31 +827 856 109.19 +828 829 110.5 +829 295 110.86 +830 831 83.15 +831 832 82.49 +832 833 80.44 +833 834 80.99 +834 40 81.66 +835 836 126.83 +836 837 125.73 +837 838 125.54 +838 839 118.42 +839 840 110.85 +840 841 111.09 +841 842 81.99 +842 358 80.97 +843 844 619.19 +844 845 618.53 +845 846 106.9 +846 847 105.36 +847 848 65.2 +848 849 82.97 +849 850 103.91 +850 851 106.54 +851 829 105.35 +852 853 161.36 +853 854 160.48 +854 855 153.31 +855 826 145.92 +856 857 111.26 +857 858 111.27 +858 533 72 +859 860 146.09 +860 861 145.5 +861 827 144.82 +862 863 75.24 +863 864 74.61 +864 865 34.1 +865 378 197 +866 285 33.46 +867 868 138 +868 869 135 +869 166 137 +870 871 526.33 +871 872 524.67 +872 873 171.42 +873 874 167.51 +874 875 174.09 +875 876 98.02 +876 877 100.53 +877 878 104.75 +878 879 98.28 +879 880 103.86 +880 117 0 +881 882 406.4 +882 883 405.18 +883 884 404.32 +884 885 395.55 +885 886 38.38 +886 887 39.79 +887 574 36.72 +888 889 196.54 +889 890 195.66 +890 891 195.5 +891 892 34.95 +892 893 34.39 +893 188 35.13 +894 895 98 +895 896 95 +896 897 98 +897 383 98 +898 899 200.41 +899 900 199.7 +900 901 199.66 +901 902 199.24 +902 903 199.17 +903 165 194.72 +904 905 240.47 +905 906 239.63 +906 907 240.14 +907 908 239.85 +908 909 239.93 +909 910 239.8 +910 911 153.73 +911 912 139.02 +912 913 25.99 +913 914 25.8 +914 640 30.59 +915 916 57.63 +916 917 56.93 +917 918 56.9 +918 919 56.61 +919 920 56.6 +920 921 56.31 +921 922 52.48 +922 923 49.64 +923 924 37 +925 926 698.1 +926 927 696.29 +927 928 697.35 +928 929 696.84 +929 930 104.07 +930 931 112.53 +931 932 116.56 +932 933 113.51 +933 42 72.73 +934 935 108 +935 936 105 +936 937 108 +937 166 108 +938 939 247.1 +939 940 246.42 +940 941 245.86 +941 942 218.76 +942 943 218.87 +943 944 33.42 +944 188 31 +945 946 75 +946 947 72 +947 383 74 +948 949 103 +949 950 95 +950 951 103 +951 952 103 +952 953 98 +953 725 83 +954 955 180 +955 956 177 +956 957 179 +957 377 76 +957 865 146 +958 460 32 +959 960 91 +960 961 87 +961 962 91 +962 963 91 +963 964 90 +964 118 89 +965 966 306 +966 967 303 +967 968 305 +968 969 155 +969 970 303 +970 971 192 +971 972 112 +972 973 112 +973 974 111 +974 975 80 +975 41 79 +976 977 170.09 +977 978 56.57 +978 979 165.74 +979 980 169.19 +980 981 168.92 +981 982 169.16 +982 983 169.1 +983 984 161.78 +984 985 161.68 +985 986 164.96 +986 987 164.63 +987 988 144.05 +988 989 33.96 +989 811 33.93 +990 991 194.72 +991 992 193.9 +992 993 194.2 +993 994 165.1 +994 995 164.38 +995 985 164.86 +996 997 144.93 +997 998 144.35 +998 999 144.48 +999 1000 144.54 +1000 1001 142.77 +1001 987 144.15 +1002 1003 92 +1003 1004 16 +1004 1005 60 +1005 62 91 +1006 1007 103 +1007 1008 100 +1008 1009 103 +1009 1010 96 +1010 1011 60 +1011 80 86 +1012 1013 101 +1013 1014 97 +1014 1015 95 +1015 1016 77 +1016 60 54 +1017 1018 98 +1018 1019 95 +1019 1020 0 +1020 351 19 +1021 1022 380 +1022 1023 254 +1023 1024 370 +1024 1025 372 +1025 1026 379 +1026 80 311 +1027 1028 47 +1028 1029 40 +1029 1030 40 +1030 1031 46 +1031 924 59.42 +1032 1033 39 +1033 1034 36 +1034 1035 38 +1035 552 39 +1036 1037 56 +1037 1038 52 +1038 1039 55 +1039 923 55 +1040 1041 42 +1041 1042 39 +1042 1043 42 +1043 358 41 +1044 1045 104 +1045 1046 101 +1046 1047 103 +1047 1048 102 +1048 1049 102 +1049 858 98 +1050 1051 338 +1051 1052 335 +1052 1053 337 +1053 1054 337 +1054 1055 64 +1055 1056 178 +1056 1057 177 +1057 1058 177 +1058 1059 177 +1059 1060 64 +1060 1061 153 +1061 1062 31 +1062 460 153 +1063 1064 183 +1064 1065 180 +1065 377 33 +1066 1067 255.41 +1067 1068 254.7 +1068 1069 254.31 +1069 1070 218.96 +1070 1071 238.65 +1071 1072 238.41 +1072 1073 237.99 +1073 1074 81.72 +1074 1075 102.28 +1075 188 92.68 +1076 1077 87.02 +1077 1078 86.6 +1078 1079 86.7 +1079 1080 86.71 +1080 1081 86.51 +1081 1082 86.28 +1082 117 72.11 +1083 1084 37 +1084 865 26 +1085 1086 88.34 +1086 1087 86.73 +1087 1088 87.48 +1088 1089 87.6 +1089 1090 86.77 +1090 571 85.58 +1091 1092 82 +1092 1093 78 +1093 1094 80 +1094 1095 79 +1095 1096 78 +1096 1097 77 +1097 1098 79 +1098 262 79 +1099 1100 80.61 +1100 1101 77.63 +1101 40 71.88 +1102 1103 90.9 +1103 1104 90.1 +1104 1105 90.42 +1106 1107 329 +1107 1108 326 +1108 957 301 +1109 1110 157.39 +1110 1111 156.91 +1111 1112 156.99 +1112 1113 32.57 +1113 188 3.44 +1114 1115 33 +1115 1116 29 +1116 1117 33 +1117 1118 32 +1118 1119 32 +1119 135 32 +1120 1121 179 +1121 1122 175 +1122 1123 178 +1123 1124 0 +1124 1125 0 +1126 1127 43.71 +1127 1128 43.16 +1128 1129 41.4 +1129 1130 33.49 +1130 812 31.02 +1131 1132 87.82 +1132 1133 86.88 +1133 1134 85.49 +1134 9 77.69 +1135 1136 98 +1136 1137 102 +1137 1138 98 +1138 1139 100 +1139 1140 41 +1140 1141 95 +1141 1142 91 +1142 41 34 +1143 1144 352.6 +1144 1145 351.82 +1145 1146 351.73 +1146 1147 343.47 +1147 1148 323.5 +1148 1149 172.05 +1149 1150 171.57 +1150 1151 179.87 +1151 1152 154.08 +1152 1153 157.73 +1153 1154 170.23 +1154 42 55.24 +1155 1156 185.3 +1156 1157 184.13 +1157 1158 183.56 +1158 1159 183.14 +1159 1160 42.16 +1160 1161 40.43 +1162 1163 40.61 +1163 1164 32.05 +1164 1165 34.36 +1165 205 31.2 +1166 1167 39 +1167 1168 36 +1168 1169 0 +1169 1170 38 +1170 924 38 +1171 1172 240.93 +1172 1173 240.24 +1173 1174 238.02 +1174 1074 96.87 +1175 1176 107 +1176 1177 104 +1177 1178 104 +1178 1179 104 +1179 1180 102 +1180 1181 84 +1181 1182 84 +1182 28 74 +1183 1184 108.07 +1184 1185 107.53 +1185 1186 106.58 +1186 709 106.38 +1187 1188 119.42 +1188 1189 118.71 +1189 1190 118.15 +1190 1191 117.11 +1191 1192 115.11 +1192 1193 114.82 +1193 1194 104.91 +1194 41 103.99 +1195 1196 37.68 +1196 1197 37.15 +1197 1198 37.19 +1198 1199 33.94 +1199 414 34.17 +1200 1201 118.72 +1201 1202 113.14 +1202 1203 0 +1203 1204 78.09 +1204 1205 117.75 +1205 1206 117.48 +1206 1207 103.57 +1207 1208 116.96 +1208 1209 117.29 +1209 1210 106.13 +1210 10 113.75 +1211 1212 95.92 +1212 1213 95.3 +1213 1214 93.98 +1214 1215 93.98 +1215 1216 93.93 +1216 1217 89.21 +1217 1218 85.06 +1218 1219 65.28 +1219 1220 65.3 +1220 40 63.33 +1221 1222 33 +1222 1223 30 +1223 1224 33 +1224 187 33 +1225 1226 216.31 +1226 1227 215.75 +1227 1228 207.33 +1228 716 201.93 +1229 1230 163.93 +1230 1231 163.51 +1231 1232 163.47 +1232 1233 163.54 +1233 1234 91.76 +1234 59 88.9 +1235 1236 155.59 +1236 1237 154.73 +1237 1238 155.15 +1238 1239 155.18 +1239 1240 155.17 +1240 1241 155.12 +1241 1242 154.86 +1242 1243 75.58 +1243 1244 75.58 +1244 1245 75.6 +1245 117 70.16 +1246 1247 211.11 +1247 1248 210.29 +1248 1249 210.75 +1249 1250 210.68 +1250 1251 210.58 +1251 1252 210.37 +1252 1227 209.84 +1253 1254 86.95 +1254 1255 86.13 +1255 1256 86.29 +1256 1257 85.37 +1257 1258 77.29 +1258 1259 76.3 +1259 215 75.57 +1260 1261 117.66 +1261 1262 109.11 +1262 1263 101.86 +1263 1264 92.95 +1264 215 107.68 +1265 1266 69.35 +1266 1267 68.74 +1267 1268 67.13 +1268 1269 67.59 +1269 1270 66.29 +1270 1031 67.14 +1271 1272 95.06 +1272 1273 94.48 +1273 1274 93.89 +1274 1275 93.92 +1275 1276 76.55 +1276 1277 93.73 +1277 149 87.97 +1278 1279 67 +1279 1280 90 +1280 1281 91 +1281 1282 91 +1282 1283 90 +1283 1284 89 +1284 1285 90 +1285 1286 89 +1286 1287 85 +1287 9 85 +1288 1289 123 +1289 1290 120 +1290 1291 122 +1291 1292 115 +1292 1293 119 +1293 1294 116 +1294 1295 113 +1295 1296 116 +1296 1297 44 +1297 133 29 +1298 1299 109 +1299 1300 106 +1300 1301 103 +1301 1302 103 +1302 1303 103 +1303 1304 94 +1304 1305 95 +1305 1306 90 +1306 1307 84 +1307 1308 84 +1308 1142 83 +1309 1310 215 +1310 1311 212 +1311 1312 214 +1312 1313 212 +1313 1314 213 +1313 1331 245.24 +1314 188 37.52 +1315 1316 227 +1316 1317 224 +1317 1318 226 +1318 1319 226 +1319 944 45 +1320 1321 219.19 +1321 1322 218.4 +1322 1323 218.88 +1323 1324 218.55 +1324 1325 213.7 +1325 1314 214.5 +1326 1327 248.5 +1327 1328 247.98 +1328 1329 248.15 +1329 1330 247.15 +1330 1313 245.15 +1331 188 64.76 +1332 1333 140 +1333 1334 137 +1334 1335 139 +1335 1336 139 +1336 1337 139 +1337 1338 138 +1338 1339 70 +1339 1340 119 +1340 1341 121 +1341 1342 121 +1342 274 107 +1343 1344 97 +1344 1345 93 +1345 1346 96 +1346 577 96 +1347 580 95 +1348 1349 51 +1349 1350 48 +1350 1351 50 +1351 203 47 +1352 1353 117 +1353 1354 114 +1354 267 116 +1355 1356 116 +1356 1357 116 +1357 271 116 diff --git a/share/doc/networkx-1.11/examples/drawing/lanl_routes.py b/share/doc/networkx-1.11/examples/drawing/lanl_routes.py new file mode 100644 index 000000000..ed316229b --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/lanl_routes.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +""" +Routes to LANL from 186 sites on the Internet. + +This uses Graphviz for layout so you need PyGraphviz or PyDotPlus. + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2004-2016 +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + + +def lanl_graph(): + """ Return the lanl internet view graph from lanl.edges + """ + import networkx as nx + try: + fh = open('lanl_routes.edgelist' , 'r') + except IOError: + print("lanl.edges not found") + raise + + G = nx.Graph() + + time = {} + time[0] = 0 # assign 0 to center node + for line in fh.readlines(): + (head, tail, rtt) = line.split() + G.add_edge(int(head), int(tail)) + time[int(head)] = float(rtt) + + # get largest component and assign ping times to G0time dictionary + G0 = sorted(nx.connected_component_subgraphs(G), key = len, reverse=True)[0] + G0.rtt = {} + for n in G0: + G0.rtt[n] = time[n] + + return G0 + +if __name__ == '__main__': + import networkx as nx + import math + try: + import pygraphviz + from networkx.drawing.nx_agraph import graphviz_layout + except ImportError: + try: + import pydotplus + from networkx.drawing.nx_pydot import graphviz_layout + except ImportError: + raise ImportError("This example needs Graphviz and either " + "PyGraphviz or PyDotPlus") + + G=lanl_graph() + + print("graph has %d nodes with %d edges"\ + %(nx.number_of_nodes(G), nx.number_of_edges(G))) + print(nx.number_connected_components(G), "connected components") + + import matplotlib.pyplot as plt + plt.figure(figsize=(8, 8)) + # use graphviz to find radial layout + pos = graphviz_layout(G, prog="twopi", root=0) + # draw nodes, coloring by rtt ping time + nx.draw(G, pos, + node_color=[G.rtt[v] for v in G], + with_labels=False, + alpha=0.5, + node_size=15) + # adjust the plot limits + xmax = 1.02 * max(xx for xx,yy in pos.values()) + ymax = 1.02 * max(yy for xx,yy in pos.values()) + plt.xlim(0, xmax) + plt.ylim(0, ymax) + plt.savefig("lanl_routes.png") diff --git a/share/doc/networkx-1.11/examples/drawing/node_colormap.py b/share/doc/networkx-1.11/examples/drawing/node_colormap.py new file mode 100644 index 000000000..6a2d28d4b --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/node_colormap.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +""" +Draw a graph with matplotlib, color by degree. +You must have matplotlib for this to work. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +try: + import matplotlib.pyplot as plt +except: + raise +import networkx as nx + + +G=nx.cycle_graph(24) +pos=nx.spring_layout(G,iterations=200) +nx.draw(G,pos,node_color=range(24),node_size=800,cmap=plt.cm.Blues) +plt.savefig("node_colormap.png") # save as png +plt.show() # display diff --git a/share/doc/networkx-1.11/examples/drawing/random_geometric_graph.py b/share/doc/networkx-1.11/examples/drawing/random_geometric_graph.py new file mode 100644 index 000000000..d39fec6f3 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/random_geometric_graph.py @@ -0,0 +1,33 @@ +import networkx as nx +import matplotlib.pyplot as plt + +G=nx.random_geometric_graph(200,0.125) +# position is stored as node attribute data for random_geometric_graph +pos=nx.get_node_attributes(G,'pos') + +# find node near center (0.5,0.5) +dmin=1 +ncenter=0 +for n in pos: + x,y=pos[n] + d=(x-0.5)**2+(y-0.5)**2 + if d +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import zipfile, cStringIO +import networkx as nx +import matplotlib.pyplot as plt + +zf = zipfile.ZipFile('sampson_data.zip') # zipfile object +e1=cStringIO.StringIO(zf.read('samplike1.txt')) # read info file +e2=cStringIO.StringIO(zf.read('samplike2.txt')) # read info file +e3=cStringIO.StringIO(zf.read('samplike3.txt')) # read info file +G1=nx.read_edgelist(e1,delimiter='\t') +G2=nx.read_edgelist(e2,delimiter='\t') +G3=nx.read_edgelist(e3,delimiter='\t') +pos=nx.spring_layout(G3,iterations=100) +plt.clf() + +plt.subplot(221) +plt.title('samplike1') +nx.draw(G1,pos,node_size=50,with_labels=False) +plt.subplot(222) +plt.title('samplike2') +nx.draw(G2,pos,node_size=50,with_labels=False) +plt.subplot(223) +plt.title('samplike3') +nx.draw(G3,pos,node_size=50,with_labels=False) +plt.subplot(224) +plt.title('samplike1,2,3') +nx.draw(G3,pos,edgelist=G3.edges(),node_size=50,with_labels=False) +nx.draw_networkx_edges(G1,pos,alpha=0.25) +nx.draw_networkx_edges(G2,pos,alpha=0.25) +plt.savefig("sampson.png") # save as png +plt.show() # display diff --git a/share/doc/networkx-1.11/examples/drawing/simple_path.py b/share/doc/networkx-1.11/examples/drawing/simple_path.py new file mode 100644 index 000000000..b7f70b9fc --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/simple_path.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +""" +Draw a graph with matplotlib. +You must have matplotlib for this to work. +""" +try: + import matplotlib.pyplot as plt +except: + raise + +import networkx as nx + +G=nx.path_graph(8) +nx.draw(G) +plt.savefig("simple_path.png") # save as png +plt.show() # display diff --git a/share/doc/networkx-1.11/examples/drawing/unix_email.mbox b/share/doc/networkx-1.11/examples/drawing/unix_email.mbox new file mode 100644 index 000000000..a3a7cf8dd --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/unix_email.mbox @@ -0,0 +1,84 @@ +From alice@edu Thu Jun 16 16:12:12 2005 +From: Alice +Subject: NetworkX +Date: Thu, 16 Jun 2005 16:12:13 -0700 +To: Bob +Status: RO +Content-Length: 86 +Lines: 5 + +Bob, check out the new networkx release - you and +Carol might really like it. + +Alice + + +From bob@gov Thu Jun 16 18:13:12 2005 +Return-Path: +Subject: Re: NetworkX +From: Bob +To: Alice +Content-Type: text/plain +Date: Thu, 16 Jun 2005 18:13:12 -0700 +Status: RO +Content-Length: 26 +Lines: 4 + +Thanks for the tip. + +Bob + + +From ted@com Thu Jul 28 09:53:31 2005 +Return-Path: +Subject: Graph package in Python? +From: Ted +To: Bob +Content-Type: text/plain +Date: Thu, 28 Jul 2005 09:47:03 -0700 +Status: RO +Content-Length: 90 +Lines: 3 + +Hey Ted - I'm looking for a Python package for +graphs and networks. Do you know of any? + + +From bob@gov Thu Jul 28 09:59:31 2005 +Return-Path: +Subject: Re: Graph package in Python? +From: Bob +To: Ted +Content-Type: text/plain +Date: Thu, 28 Jul 2005 09:59:03 -0700 +Status: RO +Content-Length: 180 +Lines: 9 + + +Check out the NetworkX package - Alice sent me the tip! + +Bob + +>> bob@gov scrawled: +>> Hey Ted - I'm looking for a Python package for +>> graphs and networks. Do you know of any? + + +From ted@com Thu Jul 28 15:53:31 2005 +Return-Path: +Subject: get together for lunch to discuss Networks? +From: Ted +To: Bob , Carol , Alice +Content-Type: text/plain +Date: Thu, 28 Jul 2005 15:47:03 -0700 +Status: RO +Content-Length: 139 +Lines: 5 + +Hey everyrone! Want to meet at that restaurant on the +island in Konigsburg tonight? Bring your laptops +and we can install NetworkX. + +Ted + diff --git a/share/doc/networkx-1.11/examples/drawing/unix_email.py b/share/doc/networkx-1.11/examples/drawing/unix_email.py new file mode 100755 index 000000000..ae95b729a --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/unix_email.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +""" +Create a directed graph, allowing multiple edges and self loops, from +a unix mailbox. The nodes are email addresses with links +that point from the sender to the recievers. The edge data +is a Python email.Message object which contains all of +the email message data. + +This example shows the power of XDiGraph to hold edge data +of arbitrary Python objects (in this case a list of email messages). + +By default, load the sample unix email mailbox called "unix_email.mbox". +You can load your own mailbox by naming it on the command line, eg + +python unixemail.py /var/spool/mail/username + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2005-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import email +from email.utils import getaddresses,parseaddr +import mailbox +import sys + +# unix mailbox recipe +# see http://www.python.org/doc/current/lib/module-mailbox.html +def msgfactory(fp): + try: + return email.message_from_file(fp) + except email.Errors.MessageParseError: + # Don't return None since that will stop the mailbox iterator + return '' + + + +if __name__ == '__main__': + + import networkx as nx + try: + import matplotlib.pyplot as plt + except: + pass + + if len(sys.argv)==1: + filePath = "unix_email.mbox" + else: + filePath = sys.argv[1] + + mbox = mailbox.mbox(filePath, msgfactory) # parse unix mailbox + + G=nx.MultiDiGraph() # create empty graph + + # parse each messages and build graph + for msg in mbox: # msg is python email.Message.Message object + (source_name,source_addr) = parseaddr(msg['From']) # sender + # get all recipients + # see http://www.python.org/doc/current/lib/module-email.Utils.html + tos = msg.get_all('to', []) + ccs = msg.get_all('cc', []) + resent_tos = msg.get_all('resent-to', []) + resent_ccs = msg.get_all('resent-cc', []) + all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs) + # now add the edges for this mail message + for (target_name,target_addr) in all_recipients: + G.add_edge(source_addr,target_addr,message=msg) + + # print edges with message subject + for (u,v,d) in G.edges_iter(data=True): + print("From: %s To: %s Subject: %s"%(u,v,d['message']["Subject"])) + + + try: # draw + pos=nx.spring_layout(G,iterations=10) + nx.draw(G,pos,node_size=0,alpha=0.4,edge_color='r',font_size=16) + plt.savefig("unix_email.png") + plt.show() + except: # matplotlib not available + pass diff --git a/share/doc/networkx-1.11/examples/drawing/weighted_graph.py b/share/doc/networkx-1.11/examples/drawing/weighted_graph.py new file mode 100644 index 000000000..74e265144 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/weighted_graph.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +""" +An example using Graph as a weighted network. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) +try: + import matplotlib.pyplot as plt +except: + raise + +import networkx as nx + +G=nx.Graph() + +G.add_edge('a','b',weight=0.6) +G.add_edge('a','c',weight=0.2) +G.add_edge('c','d',weight=0.1) +G.add_edge('c','e',weight=0.7) +G.add_edge('c','f',weight=0.9) +G.add_edge('a','d',weight=0.3) + +elarge=[(u,v) for (u,v,d) in G.edges(data=True) if d['weight'] >0.5] +esmall=[(u,v) for (u,v,d) in G.edges(data=True) if d['weight'] <=0.5] + +pos=nx.spring_layout(G) # positions for all nodes + +# nodes +nx.draw_networkx_nodes(G,pos,node_size=700) + +# edges +nx.draw_networkx_edges(G,pos,edgelist=elarge, + width=6) +nx.draw_networkx_edges(G,pos,edgelist=esmall, + width=6,alpha=0.5,edge_color='b',style='dashed') + +# labels +nx.draw_networkx_labels(G,pos,font_size=20,font_family='sans-serif') + +plt.axis('off') +plt.savefig("weighted_graph.png") # save as png +plt.show() # display diff --git a/share/doc/networkx-1.11/examples/graph/atlas.py b/share/doc/networkx-1.11/examples/graph/atlas.py new file mode 100644 index 000000000..418ac6363 --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/atlas.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +""" +Atlas of all graphs of 6 nodes or less. + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx +from networkx.generators.atlas import * +from networkx.algorithms.isomorphism.isomorph import graph_could_be_isomorphic as isomorphic +import random + +def atlas6(): + """ Return the atlas of all connected graphs of 6 nodes or less. + Attempt to check for isomorphisms and remove. + """ + + Atlas = graph_atlas_g()[0:208] # 208 + # remove isolated nodes, only connected graphs are left + U = nx.Graph() # graph for union of all graphs in atlas + for G in Atlas: + zerodegree = [n for n in G if G.degree(n)==0] + for n in zerodegree: + G.remove_node(n) + U = nx.disjoint_union(U, G) + + # list of graphs of all connected components + C = nx.connected_component_subgraphs(U) + + UU = nx.Graph() + # do quick isomorphic-like check, not a true isomorphism checker + nlist = [] # list of nonisomorphic graphs + for G in C: + # check against all nonisomorphic graphs so far + if not iso(G, nlist): + nlist.append(G) + UU = nx.disjoint_union(UU, G) # union the nonisomorphic graphs + return UU + +def iso(G1, glist): + """Quick and dirty nonisomorphism checker used to check isomorphisms.""" + for G2 in glist: + if isomorphic(G1, G2): + return True + return False + + +if __name__ == '__main__': + G=atlas6() + + print("graph has %d nodes with %d edges"\ + %(nx.number_of_nodes(G), nx.number_of_edges(G))) + print(nx.number_connected_components(G), "connected components") + + try: + import pygraphviz + from networkx.drawing.nx_agraph import graphviz_layout + except ImportError: + try: + import pydotplus + from networkx.drawing.nx_pydot import graphviz_layout + except ImportError: + raise ImportError("This example needs Graphviz and either " + "PyGraphviz or PyDotPlus") + + import matplotlib.pyplot as plt + plt.figure(1, figsize=(8, 8)) + # layout graphs with positions using graphviz neato + pos = graphviz_layout(G, prog="neato") + # color nodes the same in each connected subgraph + C = nx.connected_component_subgraphs(G) + for g in C: + c = [random.random()] * nx.number_of_nodes(g) # random color... + nx.draw(g, + pos, + node_size=40, + node_color=c, + vmin=0.0, + vmax=1.0, + with_labels=False + ) + plt.savefig("atlas.png", dpi=75) diff --git a/share/doc/networkx-1.11/examples/graph/atlas2.py b/share/doc/networkx-1.11/examples/graph/atlas2.py new file mode 100644 index 000000000..268f5d3a6 --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/atlas2.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +""" +Write first 20 graphs from the graph atlas as graphviz dot files +Gn.dot where n=0,19. +Requires pygraphviz and graphviz. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) +# Date: 2005-05-19 14:23:02 -0600 (Thu, 19 May 2005) + +# Copyright (C) 2006-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx +from networkx.generators.atlas import * +from pygraphviz import * + +atlas = graph_atlas_g()[0:20] + + +for G in atlas: + print("graph %s has %d nodes with %d edges" + %(G.name,NX.number_of_nodes(G),NX.number_of_edges(G))) + A = nx.nx_agraph.to_agraph(G) + A.graph_attr['label'] = G.name + # set default node attributes + A.node_attr['color'] = 'red' + A.node_attr['style'] = 'filled' + A.node_attr['shape'] = 'circle' + A.write(G.name + '.dot') diff --git a/share/doc/networkx-1.11/examples/graph/degree_sequence.py b/share/doc/networkx-1.11/examples/graph/degree_sequence.py new file mode 100644 index 000000000..8a767d293 --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/degree_sequence.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +""" +Random graph from given degree sequence. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) +# Date: 2004-11-03 08:11:09 -0700 (Wed, 03 Nov 2004) +# Revision: 503 + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +from networkx import * + +z=[5,3,3,3,3,2,2,2,1,1,1] +print(is_valid_degree_sequence(z)) + +print("Configuration model") +G=configuration_model(z) # configuration model +degree_sequence=list(degree(G).values()) # degree sequence +print("Degree sequence %s" % degree_sequence) +print("Degree histogram") +hist={} +for d in degree_sequence: + if d in hist: + hist[d]+=1 + else: + hist[d]=1 +print("degree #nodes") +for d in hist: + print('%d %d' % (d,hist[d])) diff --git a/share/doc/networkx-1.11/examples/graph/erdos_renyi.py b/share/doc/networkx-1.11/examples/graph/erdos_renyi.py new file mode 100644 index 000000000..63ffb1891 --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/erdos_renyi.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +#!/usr/bin/env python +""" +Create an G{n,m} random graph with n nodes and m edges +and report some properties. + +This graph is sometimes called the Erdős-Rényi graph +but is different from G{n,p} or binomial_graph which is also +sometimes called the Erdős-Rényi graph. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +from networkx import * +import sys + +n=10 # 10 nodes +m=20 # 20 edges + +G=gnm_random_graph(n,m) + +# some properties +print("node degree clustering") +for v in nodes(G): + print('%s %d %f' % (v,degree(G,v),clustering(G,v))) + +# print the adjacency list to terminal +try: + write_adjlist(G,sys.stdout) +except TypeError: # Python 3.x + write_adjlist(G,sys.stdout.buffer) + diff --git a/share/doc/networkx-1.11/examples/graph/expected_degree_sequence.py b/share/doc/networkx-1.11/examples/graph/expected_degree_sequence.py new file mode 100644 index 000000000..0a00851cd --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/expected_degree_sequence.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +""" +Random graph from given degree sequence. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2006-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +from networkx import * +from networkx.generators.degree_seq import * + +# make a random graph of 500 nodes with expected degrees of 50 +n=500 # n nodes +p=0.1 +w=[p*n for i in range(n)] # w = p*n for all nodes +G=expected_degree_graph(w) # configuration model +print("Degree histogram") +print("degree (#nodes) ****") +dh=degree_histogram(G) +low=min(degree(G)) +for i in range(low,len(dh)): + bar=''.join(dh[i]*['*']) + print("%2s (%2s) %s"%(i,dh[i],bar)) + diff --git a/share/doc/networkx-1.11/examples/graph/football.py b/share/doc/networkx-1.11/examples/graph/football.py new file mode 100644 index 000000000..d70cebc6c --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/football.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +""" +Load football network in GML format and compute some network statistcs. + +Shows how to download GML graph in a zipped file, unpack it, and load +into a NetworkX graph. + +Requires Internet connection to download the URL +http://www-personal.umich.edu/~mejn/netdata/football.zip + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2007-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +from networkx import * + +url="http://www-personal.umich.edu/~mejn/netdata/football.zip" + +try: # Python 3.x + import urllib.request as urllib +except ImportError: # Python 2.x + import urllib +import io +import zipfile + +sock = urllib.urlopen(url) # open URL +s=io.BytesIO(sock.read()) # read into BytesIO "file" +sock.close() + +zf = zipfile.ZipFile(s) # zipfile object +txt=zf.read('football.txt').decode() # read info file +gml=zf.read('football.gml').decode() # read gml data +# throw away bogus first line with # from mejn files +gml=gml.split('\n')[1:] +G=parse_gml(gml) # parse gml data + +print(txt) +# print degree for each team - number of games +for n,d in G.degree_iter(): + print('%s %d' % (n, d)) diff --git a/share/doc/networkx-1.11/examples/graph/karate_club.py b/share/doc/networkx-1.11/examples/graph/karate_club.py new file mode 100644 index 000000000..58121f3c7 --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/karate_club.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python +""" +Zachary's Karate Club graph + +Data file from: +http://vlado.fmf.uni-lj.si/pub/networks/data/Ucinet/UciData.htm + +Reference: +Zachary W. (1977). +An information flow model for conflict and fission in small groups. +Journal of Anthropological Research, 33, 452-473. +""" +import networkx as nx +G=nx.karate_club_graph() +print("Node Degree") +for v in G: + print('%s %s' % (v,G.degree(v))) diff --git a/share/doc/networkx-1.11/examples/graph/knuth_miles.py b/share/doc/networkx-1.11/examples/graph/knuth_miles.py new file mode 100644 index 000000000..0ba9fd767 --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/knuth_miles.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +""" +An example using networkx.Graph(). + +miles_graph() returns an undirected graph over the 128 US cities from +the datafile miles_dat.txt. The cities each have location and population +data. The edges are labeled with the distance betwen the two cities. + +This example is described in Section 1.1 in Knuth's book [1,2]. + +References. +----------- + +[1] Donald E. Knuth, + "The Stanford GraphBase: A Platform for Combinatorial Computing", + ACM Press, New York, 1993. +[2] http://www-cs-faculty.stanford.edu/~knuth/sgb.html + + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx + + +def miles_graph(): + """ Return the cites example graph in miles_dat.txt + from the Stanford GraphBase. + """ + # open file miles_dat.txt.gz (or miles_dat.txt) + import gzip + fh = gzip.open('knuth_miles.txt.gz','r') + + G=nx.Graph() + G.position={} + G.population={} + + cities=[] + for line in fh.readlines(): + line = line.decode() + if line.startswith("*"): # skip comments + continue + + numfind=re.compile("^\d+") + + if numfind.match(line): # this line is distances + dist=line.split() + for d in dist: + G.add_edge(city,cities[i],weight=int(d)) + i=i+1 + else: # this line is a city, position, population + i=1 + (city,coordpop)=line.split("[") + cities.insert(0,city) + (coord,pop)=coordpop.split("]") + (y,x)=coord.split(",") + + G.add_node(city) + # assign position - flip x axis for matplotlib, shift origin + G.position[city]=(-int(x)+7500,int(y)-3000) + G.population[city]=float(pop)/1000.0 + return G + +if __name__ == '__main__': + import networkx as nx + import re + import sys + + G=miles_graph() + + print("Loaded miles_dat.txt containing 128 cities.") + print("digraph has %d nodes with %d edges"\ + %(nx.number_of_nodes(G),nx.number_of_edges(G))) + + + # make new graph of cites, edge if less then 300 miles between them + H=nx.Graph() + for v in G: + H.add_node(v) + for (u,v,d) in G.edges(data=True): + if d['weight'] < 300: + H.add_edge(u,v) + + # draw with matplotlib/pylab + + try: + import matplotlib.pyplot as plt + plt.figure(figsize=(8,8)) + # with nodes colored by degree sized by population + node_color=[float(H.degree(v)) for v in H] + nx.draw(H,G.position, + node_size=[G.population[v] for v in H], + node_color=node_color, + with_labels=False) + + # scale the axes equally + plt.xlim(-5000,500) + plt.ylim(-2000,3500) + + plt.savefig("knuth_miles.png") + except: + pass + + + diff --git a/share/doc/networkx-1.11/examples/graph/knuth_miles.txt.gz b/share/doc/networkx-1.11/examples/graph/knuth_miles.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..62b7f95fb8aebea1f93b55924697e2a1958d3b13 GIT binary patch literal 20317 zcmV(vKGJzee~If|ex28h%Y1ly`{955(?9+A@^1DC#@b>*veDZJa761SI^7i>{xYd_$_s`$Fe0+cT*N^u%;%!@1J>t5Ao=Kd3bn^i@f`A|NLz{@!QMg{;w~O?_&jiy1aXN{^rx`XT@$uWo=XjO-w|xKj{r`yH{|is{4=(x7V#)vEc7gxD zXS@8jFaG*L!`ajic*{PpoQo|3EGfBSHW>)l`CW0wz)-+uV< z{-@W=7UvIVdyK%O0Ep;tr z83%v=?(zG0_CNpp>+Lw!yZFmquG{GKSjt}7rEXoOWEssIo56Y^W$sm?rXfuFCU(sUgO%o`Z^w^-Su&v*SgoTl(LOW>E*m^ zy8lS;wWGTfLOp$7LJb?fLof zhlg+D$$s&5JU4Gx_UnFZ+u4?Qz;ZeI5jSh?Qnu~5?E4zuJ_p}?_hG)(pML&z8)e*` zy&TuN)^hUp>!ptM5=(K3wYk(|E0Y=akI&z_wmj`tx4ZKkHJ*0V)|VFF zEWO48mROtk<>fr~xZWPmvahjH?fkoI{PLgU3C{iQ-1q%@t|iulo0g-;|4P4XZN0>n z#}!{$z3g{oZ3j=%LIUC;;yu<_ymFR$2_azR+oks&zi#`b ztVgWT!SeKySG)iI^@o>_Z+Dm9$8MJzPj#;AwY6=Hy^EcXOV!JM#7eBOBzuVk8s*Yk zy|iPnP;s-k_NBJ*`GP#(pa1lAJCAf0L6wym+< z+of-DojumCtY^8jy~P#ZeRvG*{Fi&h>#P6$b&J)$8+{wsu~nWi#4?vTWG7y1KjSXV zS8T}6&OadwWsSR*zOf|z^mZXw+wBf_mw)?`2Oen6b`7EFZC_eETv=BxajvXh{Ab&| z0g@G~J@~IO>gC+#?v*p1U@P2lkIR1ugAg0OIZu67fV=K%YDD7 zhb=a651D6K#ujmHmF*h6$zo+AFX?IRwg$A6w^oEgSZ#!Bv5WW?GL8(TFLgf`hSvG#hH`JLB z+rpx3M_Fpyd))Hj{oUmYgwwml<*&7eP9HTSsA41z0`(ZMWtuy3lgxP7a z)$u#trSI|62wkYvrH+4gmgx*ZsmC5deE-wCk1wwwZT}jUB5oKW+`=-AcE%%(&5N%W zBmi}D^UFGNcYLumRox9GEAlX~sJ!tO8gTBhKWmt~J$CGfB@RK4 z7Y*47R}$9c4AI;|j(RL=C6sj%EQ(O^y7B@d*8!8WRhLm&HGc4p*L#QxD;i4|c zTMIK%PxP>@2eOe*@$~W%JAkL%VD9!ppz-zdU~@IsBH z4OJcLW5p%K?$$6e_pcv5yUK94y$_sHh-M21QJ2Po9^u~H_t2vge|cia2A9HpWA!7t ziJv-V9&1Jrf!Y`KfCUco#$)O+|n$vcsyMXi(S`_9XYvJU;Y&F;luOuLm2&E{Wc_yxaHhqHP_Il z#m|SI)Ak(k9|UJxtWGaPLWkP1$8w&bTA{%wo|LEV#9$k)d%+~)agUQ*V#Gr_J3d_> zfGORU=k31bI0I!AoIziD!CA;fbHl)bZGWg4rVyD&uCI!_)c5 zq0w9@+;cqicdubph}I*r3Te0-YwsbTp|{H(_uE6qYZ|bf z`|#@!@Dp)aM#%UX(t&>XF*VE(mpwqCV{v(=c(EQ5F_enfi3lX7Ssde7{&fHR?&Z^8 zNf3VVMI;xI_JqqU5vj2(;rI`}vKxT8%R82W_?%x6ZN!ITbq4BgP`D4}Anrd1X5($b zKw%5QYJ`&yy+LwT0vN668GB!K>jZ*vjfO^|ZUpWJVSu(R(mDQs@}6!^JX@&1pTZhE z{B-#uQrt`qLQo@44mk-^w2|VlV>Lso3s)~FeLS?ffraD~1Pun$8+l&XsACKx{-PH- zBMAu;hyL(sEXK*6tpt;JDmM+YT2PRPJ=sUz$5=l;zBjIQ`Y_v~^hAuq;;jReH zI(Cbt4^I)6cE_ub=HNqd;2U`y>16z3L!aaMxo{XnEMP&k!w)Tge0zL%KLd$UB9N#p zeE1QbD%NMNcWfudmnH1+PzYkdd57^NFb(T(ykFAP^_D42zFcYeD$_xvhDbUhmaq(% z&Q)bBu^?E&lUS1r#&=^QoBKxc5PBRwc441G+cZHTUcD`Ue13d+&PeF@B*qbqosj`u z<6X#*f)z^NjPVL>i%V1vq(@1BJ zEyT_iQ-uvD7U2MX8<)imV#nDV^#i|m#N&5d((VIp>oSZtxsd*bEh{{C1U2YD#Dlp1 zW*TLviJ5F?rcmjds;h-8ul%&~JAGzoP23Ib2_^pO$H(VeuKO!coc*}NhM(6Oeqs

VvJ1Hyf5Srtk$}d-a253Xh!x{g1KYHBlYn(W*}Ga43W{4iMMY4?@NBMMBo6U5Pi)KY{tYbd z2rU}r8u{J;wrRwrC8MZjT&4lREZ_uq;4lw^*gbOK(0KIAR2%7WF>rbr-ybtqLqgAp ziF)eTky%2x%9e@Mz(Wvh#@~lv3w|R)g+(xo5Ry>1WKtM9kQrz!G!Az^^G=Zu$E&>) zkox)m8x|t6`GS|Yf{nDe*lOOW3h>ivY;$%Jt_mpl#wDb%N(31x)qs3szXU?eO; zI0hoSs@U&D;Dk3BFd@N_&Q^kNRz)vJ^jvrKtY-2qIsqya!Ib%9$xx$efGZ*~%06Q~ z1N0*^+>*;0f)k?o@$vcb{XMYB@4jwliBM(@bABDM|I1D2L#oUvLx{rtXxexF>AdnT ze6^Z6pdZ`JDlyte2Cz$})i|+GIS>cG90JU4VctV{x#(d)Xk>2ey(bWbdo@JZ0Ev%y zm|PgUjC;Eafj_Si8AeP|Q1Z|dtq5{^ ztZGMCYOH%aE2$*#+t^ij{Iu#vZi?pMw2F8L=CJuNxlG}p^Csc_XBedm$O)_vkj?DDHP%w3on$72Kqs439Q@?na@!f9#iq0@P z5oTON)WQ#T94>J|T?J6hW@GQjmxfEZ<;I_gUJ<3?PU4zuE~a;1ML+N{A%i#t(iF1u zFbHBj;#zyA((xG+L4k!WT^^Bb+&mT^gLZ7BuxHBI>7NL-Kq_MUQ76Jufbx2a)+3i) zzIypcP!jg&x0DmexLd#OV*u83=MAAsv#GP_<_Gig@$5%t(84Dsk*jd6i9ObW4LBmR zJF3FI&7dTn3v`U=COr@9f9 z)1U`Si7zIQT2BJ}9&h}^!*>&e4i^#z_&BNS2*3bj^mEOCofN1P^Pn)d$mpqGiK~LZ zY2CLopZnNH+LqAbg)|~`Bl66Ac??dOmWYLFhABAAbJdO?*gc|~Vl;Gi*QzZYtqsEz zS)MXfSnn}1Hs%tm?_XvqEA}{^Z@b3-Lhlz$Ir%*@ zwC~#uw%CA6n>bHK=3<@$%p@cUuYnU6aEYi*Q-gdr6oT|WY_xa*6v1p>l$gz8EGsY3pXwBTG*Io5RcFS9u`7{-AiLfu=X=}rdnk3qXXKuVmXDt zkIzxJs79pj{9`d1a7HY3!4SDn8sWk)9fjb2`qql zVJ~_OD83xNXa##Tkn%{LaE1Dp!0WHjC6N9A-7RJdmLdXon0D{OE&y!&47# zLXM9w=yF&k+z)|2@y~%25PN`9;m(aWRzj~vLPg*bt4B7JNJ^OQ`|p3a&(cgpT6Yn| z?bq1y$PyP_EcJ=>m-?5@{7NK4#J(7J6M}9vBUUo4ysZkRaoI*ZxtfZC?MO~Z^kFZ5_ph(ikz!q1L??HBi*&z(F@s*}EY6Vef$jp;CJOMik?hvCOL0f^(wyZNZ*i*gFqNwCJ8NU&@-)-BXv zSYyFd0=+Kw>^(B?5N$uiNH=mF{&qHz4kN}MKkVjPrwdOCnyYV7ti*!6d%0ES&$?0N z+VJZgf85BpTXn4mu+=L3gj)wZOD{;!FtWl?GnR;S(Y&W-eeCGga@bk|scuoB?P)%W z*j5u;J8}_qtQwEQa$3Q!vDMwj&(d}X-AcR!lG3Okk}DMcLlB*MK^dlh;10sk=iV~< zh-*+2(eQ3oDC<%+=onziqlg>u1>zpN@a0b*@85lTeV9l~ILUCt`;`baa;~aDE1LX* zq5w_Ces10m-~{j7DLa-~YqVt4^nvE_)x{;SCDCC6#ij^f%bG>MP)RViK8s)nb=0E~ zQHI(jTpl_=&1cESs53(#!m6lZJ&e;ZWaXTI0}+&AwT{)bp+LP}Owz5gX|;+zsvbr( z)X!U8zWnk2@%i;H5ilikR*#i><#r8|SQnzdDmc5Z*6XPp8Kk3-?%ld?3E!O}W+_)k z!u@#oQ28(|@f%bGn>K({f!OwVjs_%<#)xolKP*j0w7h9DaRNhIiW;q&mkw}yrVs#K z#ImruwWkdRsxO8R_qM_r|dxY%LLhRXThE0aPLA6=W)>Id#}Ba3YNaAwyhc8`jW|{1D+D zFnc4E-@{b4)zXsD7@^dbSFM!N;T{lg;;}4&r8OX&Vr`2xZ3~74&4V=)cNjIpGf^iQ z0U6rE&Ol7S-Vkjo92l0JTnt@5gr4Flj(z#^?c>X*_Y;_>ptX*17hZN;BOdL`Vccfr z6)7wkz!f+kr@RMibbI;{;6+N?hbh~dkVN>RW2V(sjG9UbqW%oLoafSQkZ(1k=nI&XI098$ zf^hI5(e_hYP=&5zjb7d*lj@7msPIH#kYiJ>z@8@*C~<5J^TlrF{^Kq7U7o&t{blkH@o+ zrm}a6o>sI{xyrkvu@xbyq}l|v6zf97Sb(#nlPnbaDiob~y;ys8I;4QuL1G1&2AW79 zLy$yVN&sxB=uCGLtM~BNhv(PJ?_VCD-zKMF4+V@6_ZqJlGG9SNht(HM85pe5^~fx( zrBjlvf^R>ER0h0+Sr^I<8oNjjp#FwP49w{+r=?}!oTx3TgPj=hkmFWf+QdE(#FK1; zFi9(Am>ai^<F7b;7|dfX5^Nr@{leqTsHX%}`@B&bBIM{gKUew?~2r*dOC9h2p>6l71>IuHr^9z*Ml=N?T^0=*i(@Jc zh!9e`9J`RC=t>pi)wGo}Yu&THMUJc!>9^X>#^38e%I-fULI&{>#^4#Q?HVb{eohS! zn?JxnPH0V7YN{_3y%3#~SY#tYW@q+9M>m6pkn}wg4`J-$+LLGr_l(Ryw2w)_lYRnv zza?j=X)J*(qNp}YNcs;Uu4hAy_nlxflkF6>Fj z`e~(uK!E@$@g$2fqPmDU857S>o-h`2ysfx;6E7il=@D5R(nYQ1AWo#STN15=U)D0Q ziIYlF#fh3!QLDilG%{Hph0IqFJY<<@w(fb6;WzL#Vnq9a|B-hYj=p&{856)Jz`+L; zm~$3Ysq?XaIux`GF~>>&-J1Od%(>RPh)f2&R6@UlJtL!|0y7Q~Kv84WW_+e}v2y$d zLn@3g9ujEyO6b5yzms{u|kIpj)T?cW);+MGV>=aFTo7XyNW*_S!uG~uV)JrwMaeL zDgYl^mr78EZ~~TXPrzOEB~N0R&vFQfmINufq0`b_2+1mF;yA~oUiO3KTUB0CL_l5T zn_M^@mJ33Ks2mYWsTE{Gke!mdiyA->6I>rwE!l!5m8M{hoC>NcC0DQ|@*luOoFCg)IxQ@&K2*s$z)Jz`SQDk?R5LjcSCWl|}^E>`c zK8jK)q#O<*u117riGf5e7rrHvLI}IUI;`;ikdz9UDN^1usX>V}@2T1LvTUd3_4bYoj%uTk$jD;Yq z?x7s(R$OunJY{CUaY8UuD@^l(AsESu^V701tCckKy>+IodO`&05L|@l104tM>bBs? zyZOtS7(*+OW<0)p`i~n|0wQr-^DeZfU3(-Hp``{uSa8|Dl-da4vJ_A(RuigknOp7I zV-Zow$Xq#VfXCXxxL{5Jr@;tNQe1Hs6fI!atO@I&37IH&*d?=$3A+!mUOOc)T_D!Y zDvXhPE$OFDgL+~4p6u1E25cg33J}*jt;i>@kucMu^5%AzOh52`r-jQEwMtsyGbgwZ8{ zXCsE00qcN1L&D5*{F+q3EI%hEKm!YPJR+P;D2Z%Q8u(tN-=DsbAf1G4G zU1Gf7Vuf9Eoiw1yLPuT4Sib!5F>>T=yZJSs-CiPGI`=CUG5i@yOQXRo^&dte+Q`jZ zxu*LY=dA{WS1T01<69(&LQD1^S+i|Gf*f#4iE)k z0SYT&=oWKReW@XsMd_`iF<_&^(S;_hNn1QKhs3(CH>-8J0_P&7jrJ5E#Mt8`ts(j{H7Jp?(2ws~=|BJb*O4Qv zcaWrxD}{Bklp+lqm@tIS7X%Sed59s_F)Qj~7HSyH$*ek2VC`!>`ajWB;Jqk&y-b~E#VXV3( zqwF^X6hwGOm0TaMTBaGa#$G84scP~E0snowY<7~tIVL-3=ef8{wPXev$Fh#$I(bB; zy%20qU?5!0^5w(#kIyecNdNYAi?{)PGsdOJ1n)SlvV_E>>}k}j3dA)2~xG zY?o{U0k$yBglNbPXiaU)SVc1NX-m>N!IGdITVxl$mG4CWYzgAXa^^Z2ibAQj{aEdf zU0IY`DYua0_uMlKixq*I_;12vS=pRy?%e{eboa7gJXAL9{6LfB5LB`MBeR#*Zgs-! z!OeUW@R`fOPS4IXATYES1PlSRn+;qd>FB5BOzS_uSz*E--rqmb_W-644*{k?Q%1P5 z1ungT8l*H541^5(XfBA@pLUbp0w1{<-4qCbC3b01sYmu-;7W##9g0sSvCcX<&01_H zC4CE(S-(1sf!BDD3~7e_R8)n01=4(|I$gL3HD;?5gg>O}unA4JEy)$eq0cS{OZ}Ro z0^G!3yx+cYhsP(@Be!2A-ITdf!;%4;xvOx7mhgXI>xm8!TFNnE$E zZ2yNH4*N`0@%N!Gk!VQI#ooZ5tx&%5^QL+7nJiq)S<%gcsemnMgL|z`BQhux0!`Q+iufae-&HZ`SO0 zunArk;3h#Q>wsaBZfz@cici|gYQkJ@?pn;Mlx$})D8^CBrUz!1pfyaQ3@diQo<%+@ z3Bf6wi08m^?8hW{iEfsiRvZfoVdN|ibhi|Stc{d_RqsALJU_g9yMO;v5<)_??!rXv z*9b0XskBmuIP|e)5-b}xb2)3!5+QfX%1IIKk}kryj3flFnNcpRJ2ER7IaOE}z?2H|k_8V75b2oYt|fj8OwfS#~uZ2%k#2-EBy zu#E_c>^T|zHHpMD8(B$kJjIc;9(wJw6(4!G6`Ab8 zKR&-bTz+%^@$n&9;}L$|g$LMUS2oLKN1vg8sD!2dBALJCD`j=cc+{po(de?eS~^iS z^N{gD2*rdK+S}|kJu(l1g;fDnq!rnaiuFFd*EyLG=3={f=bp7O8FW?q75jK(`#>#j z^>NEYR`Vv64f>JAp<)4aLH~1jfn|v1>>6FyN!?yt1>P%uQp~4OVebGOHP(Ml=BaAMzOCTx`zA``4GZ8!NPJcM%sv zxYavD6Ly=>0n4nPK7TXv&43t45syGnh=H zed<`XS-4syqykv8o1__b(Ut{%>F|W+)kh+JA#FEwtH=*9z7XJkk|O8Ri~(hKHjGsGCW~AirR_ade!QA9Ec$#8 z32J85PI@t>qjq7%fD(WNq$Y<_7UB@FmLb*3k{na23Qf2QV4ooQaF#~8BMBuER&Vqq z>Y3@kdbUG*o3c10g{oqzr2UJ>=k5C!c%#4hIzq;NM`pB#wvBz-n}SIz7NuoJ;S_e~ zmUveR(jpx|NFYD9?T4L(F%OZ0=uLu*-862Bg=gf?DHYj5EOoz4Fi?uQ2<~auYgWXw zX&DM?%^@0+n5Fk)xRez8Xe5CMQWDx)uc`??D(E1{;kE&9xs8EVhJMHFzGmIBFBZ&4 z288|OMq#IzfvCbZ2`MX!Wm1}LP=pM#bif^W8weagnY$RNv353^_1S{dg<6d_BmEX@ zmFQAy+66EJX|=aq`^Ejozl5z!z1x`oL*ejxhGs`VVQ&Y(C^Rp1cX_?gWiaS4)=;j>*DBR;eDDejQV<+*Y7%Eq$fR<17<9^eVN!>JXWb z-bHB_`>8odL^fF)GZEakZSLpKpXXji%1wE>pI2U#@)*U`J&Wn_NJl2pm6>tX8fMKH zcb#FpOX)nW>|rpWcYpz~o?TwVh&g~Ey$xF?q~oe&C+<4s-d(9u*RiG+Cg|;{4a;&$ z4rG0sy!KrNU7x+`R?{UC?berzV+mxGv`ZG&_MR$I0YPQk5GExX^t6{+E40n#*dlZ2 zZ;^*&E_*m8pI?J7*1RoMLM(N&>PLzce#mAHNaE!jEs-IGE!FmqyU?=OPs`T`r2PSG z_o|SueX=~)_123lg+A1e4ZZxY7wGtsWE(y)#Oiv4k!QrknCz>pgZRK~s*^58nOH~L z+>=c*YLY!f4J^e-<~?!B!@x$CiFgvJC=WIbX zhDxxOTI;w3fK{UHi6L_j>>l9Q5bNB8!k9{73N7Qu+IZ4J4q;{Wzms&_eJKb-SJiJPVh>*n2O3Inp*g_BiTW7{V zZMxsp%B(P;T%NHdYumm8r!B$JX@X&pNvp_tvbJ>{8VM8OT{yYH7EAQ|6y`x{4yS8v z+1Ad?xSm~E6~}E7lej%pL}Op$7zbyD31w5?e|r7yr`cmklT}FQl~FWw3i9AILCzvw@N^g#n|=e{q2s_w z8_CAxRc~~004RqHIk&Jn5@~bj3H}xsiri*6C$LFT8v;Z`*VX>+`4+~BkcgUdF^QDy zGNt@7Z-H6nWw$va4=Vh{0Hqr3Tz>iZ@a*ib0b^LQNr1;G+yB9&h`Kw<)x<;8Pdl~Vk{o9!Rnsop3-+XZOuV_=LW}Buu6RaG zzp2%&lPm2}kw|94t8GcLy#%s_()QfoUp#z#zW@0C@p?*W6R(xSANEV+yAIEzJYJBlWmspX_e@{h$rYfH|TfRPaJSZ{4I zW%4JgT(H$PHi$1)5e%BC=#h=tpx5>NcnKvG0UFzbp<-8x_KceFW$4{?!yU1HlQLln!6tdkm}l3 zW|KCWxFs|=s_S%&&t`v9-P2DpIHVb`9+Ube1fLWoLGW#f7n@v?W_A2%i5QzuC2?%i zrm2(JwtMEZglb79hkr6Oy7KzSMco}y>37A#fs`bR1FWr%P&g;PVE#%>PVe+DUA(@tTF_92hOw#+4l?#syudfFRQbo zJInM zp=jOFRy`>Yn|{r4e{eP_?b^heN#Ou%LH3;J-3piZUN{3;-pEPe6%*9Y@_v zP_Ey&ra8je-bmEW?)>D`G9P-~7BE2@y>CRpk#iFnY9pa~%_{zWTqe#u98;7XZ!qfR zzEcmiCl7HsJ*#7rGH1WDQ_!<84Ja%Nf{30U2qsU%!UHoW9b2aah1-^=3vi(HVHn~g zqXGT-o~6T{oeCrA1tHFhT4UKSyu2IBxho8`i=-Kb@w#3^VIygD;JEWOLG=KToem4< zPJ;Y)ru8p15pF_GNq^)w{@$?CdF>9yK1;eM8vs;UvS#N{fS)=}h3#yo({&`(n+eI+ zKKn9Ew<-SJ=0sWKsabzGW=Xmv&Nn8sX${6fa$4V1J(w)SpVmdZ`F^9^*;!$w9f}A| z0#4s5v+a-gr|0gO5z*azwyd!3cH*@%i>t+fAlu{;9H|2%!)RkncE{R{lx90O#5@(q zoD8bYQ2R0HF|WYFK!Qp@yU&{Q;e5^}$m>&_bBGMdg}5&bUGr?1-~E9D0)Uo7r>-I7 zP}Q8V;lzk47+SNd$1*4&eU__^+3AX=V=STS=g4t~HSNn}7c$~xm4+d0Q&@1bbd!x^ zi66)bDb^d0Tf2hx!+i}|Uc?{B)*0w^v9T^QHukn*4L=k4Od$n9WBtmx{bKSBKRh-|Jsr(7AQLmH%E>=gp#(_d+z^jza zmfTIp!o!(rV%8&1vN@+;FIcW&T*0I%f)bgJAc)crOk1r6%OG}8Q1wll16z)qmtj&1 z>tL}2-s~Z4vlt4Mc#*nt`ThN;rwGk{ix}*85oE4cD$#At;f}Td%?`Ob$tpSMJZZFu zg>7new|_{0s7jW!B%cjZr)15Uf!GCW^SRw0CY#%lRT5JN3b)v7Cf1Csf*TJ2p)P7S zRxJk$R4RHU`EgLe@h{dH*NKeJkx<8MJ`LoSkIa$Yk}z-0I-~%!htqDjCj7BwWPaM019(pb#I_NH%YK2gj})>Zk3y`I2q4FMv{%xbIMI(>q17bygxS=)5vMtF^!aX z1O{}YoK6>BXVbII#mSNjo!F9APw9+AT31-@kZ5w|PiUg6BaOSG2PKbb*AHXqHaC}w z;SV2jcOwqF3roSMs_^wh)0ZUo>`6VAt1vu^z-;maW$NM#ij4_^#gd$|mJ_s`s8cSV zj{}k!CV#Z#2?(3KzEtWwC8)~O06gPvbX6QOzaRi_wnx(limOS^ah3mJ6=*mCvO6le zOdKP@ANh?Z*-;a$?vt%XtEgv_ZP_*hpznBeV~0EfL>mUk4Xk+Xo&q$Ia^j;qxE^7o z*tI&}{WP}IxB0vvb=ys~TaLK?jt7{_B4KD(4#XW9!$!AY8C)zz0BM%j(6kf4k#(OQOL!kzm}= zILSdJXW%7JUc-qm2|LIY1Nt}L$QB9|uGKk2lD4euxf@bPtyA?012L9}0zDOcP)3i< z5OEk5X;>_tkEjfE3UlN9^D6Iq^_8(WxDe?qpXUHstI62T4cp3`{+-hz$O`R5r1!Zj zPUlU3!Q4$}d#_H&&AiJN-7{Ckc(T}X$%$$<7h%8Z!onWTq0_NA`fZ4e>}#T$`X-WT z!*%$Jgc24BV82DsvjD8@u1&qkrQpM)}iM;j6cL7fC_AM$Q%M zYGdIxmqXOsW4Edh8VFgV>MDV5+vYG~p`o^_c8=S`K&|m+IpKoJsRZO?MoK9%*%6pN zk``5*5M~oczd1!&5K_|$Nrp#;h`!t0hoOUsTT_t@E5(j-__`|-!&DuVe zdC=DBNG$5tbv9rK{P|dv8_tB0;gjdkE3oFe8n#(%qrFXXx2K#O)}@hL2bBq5i+!v@ z2H8MWg&~AXnW!e~aMy5Q#0)Qtzb-jAHm5P2^PiT7jU=UO%fT$B&toNNCRiVgK?%4Gkn?S{Dsv!5VEPy zdH%rPCBVaaB3SDQK%VFsp28?N=Ln2B`9X(e(a64kM}Mwa%g$g=ip=W3nT0(!CKG1J za}6#zS)J7skz8}+M-GDq>t##JVj3)SBoDgW zXLD{wqfUT7d%hDjiCmA&miw~b&QcMZqRZNGWLYWHmC99^2bMv~OhNehr*A*KzCAwY zxL3G1cfD|GVDY3g&s7k#x4(#{P-n#e6*l`Clhxg8lG%Dr+?>ID^A1NR z)?_o(BWGJUR&b)$9*&vxX(Ps#e66NYDvl%D^KePW<~htE=QK*jv{%6*Z0mEIWI@#I zQ#g~qx!T4bk6D*`=21ka-0~b-P7ijmuBb{SPTRclXW+-;-^f#MIE7$ItQhVIF-YJt1pG3nVbW*g&xieyB94*&veWQ=KKzGDp!13hW6Y@k3i3He|=tq*IIE zP+B+v_yy{+vamY{La3Q{vEk4ba_R^)q|OoGIg^^m#KCDZ)uSbCcfdHpK8#-dz3Cp%DCtCD@}x;f+u7#+(#?gpTezr+D?v(g9hc#PSxX$wXU8aQ%% zKjd|Y*dq1jsZMn3@`+ItfXhS*?L0=@bIe7}IWz)!(BOz0ciTmX6?CHA5iWL0d5E3! z66XmqK<|xC>ic&bcI>Esnf3bsr{BhvcF~O!K&7=c`8-DwORE#M&f8c+y9o9?b7GY{ zOx&L|@xv2!{dm}S-Z`U9GwO@;|Cp&^j+}$K<}_M-o%D)4r&G!n9JJ2)2tv^|@t;Ye zC=)Xt*$z7*BDw%;vjOkfEl>ckDm}A@> zs+<)div$+W8)P{7%LIwTerqXk*4U~IkQRyUaH|p)?Uke&KDJpOO!9>F2=da?;}na< zuaPw3EMQn|@$R0FS)`#j4(||mOp1P<_C-CB$q#iGi{5YQDT{bGM$L}oa-+jC6QYrR z8k*%-OI`l(>Fw#BsYwKx5g`$A=1sUbw@HAjtpOT zQ)ZQ(>9561_vGo^;wulNk^PM^RjUQ@wvkgSnun}y_9l&eapFRkHcqsNH8BzI>7(p& zgJn3+PcVH&llPil+B@$=DUyzo8Ozw!Gh-3}mM-({%%%`qN9`$S`y6_{ITdUye|UNQ z@!{zyJ3ZRAdhSiThN+B1Jg4`yTj?^Z8nUNs#cbMeY@LM;PoUYefh$Kz?KKhcUd956 z7uG8&k|sBah<>w=9Ft%uijmF}HIvL-GRMe%K1O|MUx*@Y$ACOS zeq^fRo(H(tcXwoY-{WllM$56c!7>ZUK@a(trc8pKM@oyE%Z0Yn(tgy0nM=mqQo^T3 zm@5hPC-1iBfx3CV)Mpk;f)e{=d}@dl5ux#;&2a~g87kQWo~_FvE$_bj>GGSGPmC$% zT=($Ocac=A*NyJFRJS~?8k>eklV(JnqPGmD>bl+9C#^-?2rn& zbUOQ_7dl(o6D-+pr&t)964U4KoIU5n zVrMUC)$!w1YV?T<_Rj7JPX1BZrn5vo9xMRwG>JO;VJ3yL9W!&T4-Hy6%GNr zp_-;B<&NE9y#dd$WE`U*O68jRi=0VLXFv`2HvmM7dxfs z)i0V>+qkgUi43$1=JT9$@9H2?hwW&noV8?ogvG(+eABVcsjjNOq?co4$qIDU0bXlz zVdPSR4E9-<)mDvcUpSR|!dGblm0V*cbMtTwP=DC59;ww7eynK8=&sMMJT_nTn6?vq4 z-zgTkl&2N)+%_w$HvoMVYg&_Y4~u4NGeBGI?cp%b+u-y(mc?-&hA?sfM;_^9l_N*9 ztR}PB;btL)uYyH!gw^U<^t;>~p+<;>z$P+G<@y5-+a+G@(AGZNGw8?@I{ZUni4e{_ zToVhqdoZPKwuhiRb->|(LUGGB9i^LcoC~Zn$@VQvA6uC~Q=XXVNewysS>!*DB(Gvg zmaw7N<{WA)mGExObKX)Jx)ULLj%ZuwWLqtbmHbQ1iL9c^maHW|Hs8aepFLdYn4>eC zyekK(+1_AXP#r$dRlg=JJ1f3U zFw1#jG`UL5STncFvymP4U$f%A=z$%LtsP(UScrYHp-mP%irp`H8cfss^gP=v+3`I+ z$i7hjwpN`ZQ%h#K$Cz{F)}+9kY4~%1T+3s1j!coW$0WSBY_HqQsL#i^Q9iI};1L76 zZE>EbFr3{yr{^U8$0<>5GM8JP8f|4-r{<-t*4yu=YZEJ3jYFMWM%-+Vz>e<3Sc`5-&nwTXm%OfhAxv)taZz#^dfgbzvX#8D2>RDBUS2{3BZ}7 z;#+o3c;355qu@EuL@gX#ea>^splVT~l08@*7nAKcP7670Fw)u0CIBD1X0N_1S@|@F zf%xLMQ?m~$bbaKT)7>SlkA^x&C$>D89bCfhJ85DT=fJi^J&DMqzd2!Uo#BlV;!uRc z8Ef0*8|C~vjLygt1sYR|v)i;y;KJCOP2z8cwKgBnRysA+M7AX- zfo8~4VYz>P`R*ad{6}hWcZOSK>~$5N!_{sOE^C&-?b@e9@^G?T&C1pBd9?aj=D@Kn zN0O|Zf!;m#zyV`6b3pQx^(xaniBiC6PW=Ya@c5ZBvOBOkdczL*6I>1p*IS+(O0Mde zwd9+QWcA=lTGO{1ob8;C9CzXb)P(3}9E+PgEH`iRAgCcK`dAA5ac;f1o6N;;+ZLwtCW7Iw~TRb@$iL9wtw&Ub;z?F zSGVN7uSwJO)LA^8vl5(&>hVH(zLy$&${pLXmWinrR*)h%=Ey*YGGm<8J`X;_-?DPB zz6p9xP_~g1h`=w*O^YL}dq()XNW0IT`DoUraZ0`B>F9JFEe?Z5W*i!**>P-(6#P}# zWS7{NGm6nsD}ykp?fd}KW2-$0+98)ntXwwRP({ZlQ@rKLxv3cTAm`x?>28aoA`s1* zJWZSLji;Mu&aD4QY&^QLx4Rx%5~(tS-&oDfQ({-&#;I>`pO3uvkw)9DtC1s}z$<}Ai@Z)7Q_e{o^YomdCH4@MGx-%f>}Eg4)MJmm4YQKt0eF?1 z(k2+>IUh1TYNp+Ub9S6rx$%H7nr6a{XWVvn5Q_^!O z3~xGmJ7BpcCs3_4S130qP#{ePDUr0JFAc%POxH6{%F5H(tdg%5P$k2x6EL@r?hrRN z>doD+d9vBsZ!JU)4U+<>5ve(Oq$k8^t59ZZwo;giOE7dOH?gfO6iEZtA$`@Bc~_ae zFvUi^W6$wKIRx9ne{B07K0H1@Py-++=4i3~*dn*y>hgP+@^ z63M;JaeF19jya@bH9EP7{lVHe3_gc+uk0+YHMw3@$U&OPcH@W085MJ=lC{=8CzRH# zr<}7l!@(FePev~}!cB7pnk#3>bd4!523pxxpHsx}t42Od2DXW|Tj%p6#_d+x_kes?PS8ecVzpr(}XGlt=OidvRGJkW{#+dqXj+np@b3-K}lxYNIPM~v*-BwgDd zYNyMdZYMeRc}}}5ZNVc_>FP<3!~^F+2}rQZapAT^$--q6w&nOTaj#|v3E7zQ@Ep$( zFPx`wYdBZW_{Apd4vwEdeBEaAQ!=?vD^J23-I*1;EpM`%YRfiS`WKCLw1(DyoQ>(Z zvB(UD!($HTuyCs+J-dj~PsK+iDPag5K66xy6 zWZPyMIiPlDhq?@$eICYO{NNb0WQaRTUoo^zx2I9h$-Exll3paz45?E+S75uH{9^G- z^J}DOMX#mnF|Ezfq_)aROLIC7&|Nd0&VwF3R!r(7h>FHeu+CW*vMF<1Mo8MXwk3V} zZ(oP0;rzOdBZ&?QtBdwF2VmKrt;(j>=aww@5)nH_yO9^3=mXi%!*bUgL#g-$%KV-QEI%q z3|%2_hn?&gxI8ShXY+2g>9Qx$>`YwDL~n=(h>?AA-c@4@ZHOiLlUK^z`HnS)wddadVAY4tpUi3U$beg zIciu^pTvoCi(pIwv)SY>3FbDJ+%D2GM0B<(aq=OK957>Wj#IsV?^&ImE!^5&XjWtP z1&gTZP{fX$RWMKPAfX~?YBGjS5!pgn@ z=zHxX z+WlW2p7EcKu`0)1?8_#3^jumd8f7B(JWlqGC{l;yu1$Gwvir7uBCorRF%T0d#(5dL9(a|%%h%!!&As%@hIjpDb#h9oN@?a<-zQk2AltwCP*b;$IR88 z+>DbzOC4}hTMApANotFKcZYBYo8ZYI&pa(ra%x0c_9e&X!!6KRNpFCdN{DP|IGg@< z+pczL26oyzTXtLi@$%2l?=LUkT)ugHdbs?<_wn!R&)(m^{loHq0Bh)0JJ_QD0BGF2 A*#H0l literal 0 HcmV?d00001 diff --git a/share/doc/networkx-1.11/examples/graph/napoleon_russian_campaign.py b/share/doc/networkx-1.11/examples/graph/napoleon_russian_campaign.py new file mode 100644 index 000000000..bdc8e918f --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/napoleon_russian_campaign.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python +""" +Minard's data from Napoleon's 1812-1813 Russian Campaign. +http://www.math.yorku.ca/SCS/Gallery/minard/minard.txt + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2006-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import string +import networkx as nx + + +def minard_graph(): + data1="""\ +24.0,54.9,340000,A,1 +24.5,55.0,340000,A,1 +25.5,54.5,340000,A,1 +26.0,54.7,320000,A,1 +27.0,54.8,300000,A,1 +28.0,54.9,280000,A,1 +28.5,55.0,240000,A,1 +29.0,55.1,210000,A,1 +30.0,55.2,180000,A,1 +30.3,55.3,175000,A,1 +32.0,54.8,145000,A,1 +33.2,54.9,140000,A,1 +34.4,55.5,127100,A,1 +35.5,55.4,100000,A,1 +36.0,55.5,100000,A,1 +37.6,55.8,100000,A,1 +37.7,55.7,100000,R,1 +37.5,55.7,98000,R,1 +37.0,55.0,97000,R,1 +36.8,55.0,96000,R,1 +35.4,55.3,87000,R,1 +34.3,55.2,55000,R,1 +33.3,54.8,37000,R,1 +32.0,54.6,24000,R,1 +30.4,54.4,20000,R,1 +29.2,54.3,20000,R,1 +28.5,54.2,20000,R,1 +28.3,54.3,20000,R,1 +27.5,54.5,20000,R,1 +26.8,54.3,12000,R,1 +26.4,54.4,14000,R,1 +25.0,54.4,8000,R,1 +24.4,54.4,4000,R,1 +24.2,54.4,4000,R,1 +24.1,54.4,4000,R,1""" + data2="""\ +24.0,55.1,60000,A,2 +24.5,55.2,60000,A,2 +25.5,54.7,60000,A,2 +26.6,55.7,40000,A,2 +27.4,55.6,33000,A,2 +28.7,55.5,33000,R,2 +29.2,54.2,30000,R,2 +28.5,54.1,30000,R,2 +28.3,54.2,28000,R,2""" + data3="""\ +24.0,55.2,22000,A,3 +24.5,55.3,22000,A,3 +24.6,55.8,6000,A,3 +24.6,55.8,6000,R,3 +24.2,54.4,6000,R,3 +24.1,54.4,6000,R,3""" + cities="""\ +24.0,55.0,Kowno +25.3,54.7,Wilna +26.4,54.4,Smorgoni +26.8,54.3,Moiodexno +27.7,55.2,Gloubokoe +27.6,53.9,Minsk +28.5,54.3,Studienska +28.7,55.5,Polotzk +29.2,54.4,Bobr +30.2,55.3,Witebsk +30.4,54.5,Orscha +30.4,53.9,Mohilow +32.0,54.8,Smolensk +33.2,54.9,Dorogobouge +34.3,55.2,Wixma +34.4,55.5,Chjat +36.0,55.5,Mojaisk +37.6,55.8,Moscou +36.6,55.3,Tarantino +36.5,55.0,Malo-Jarosewii""" + + c={} + for line in cities.split('\n'): + x,y,name=line.split(',') + c[name]=(float(x),float(y)) + + g=[] + + for data in [data1,data2,data3]: + G=nx.Graph() + i=0 + G.pos={} # location + G.pop={} # size + last=None + for line in data.split('\n'): + x,y,p,r,n=line.split(',') + G.pos[i]=(float(x),float(y)) + G.pop[i]=int(p) + if last is None: + last=i + else: + G.add_edge(i,last,{r:int(n)}) + last=i + i=i+1 + g.append(G) + + return g,c + +if __name__ == "__main__": + + (g,city)=minard_graph() + + try: + import matplotlib.pyplot as plt + plt.figure(1,figsize=(11,5)) + plt.clf() + colors=['b','g','r'] + for G in g: + c=colors.pop(0) + node_size=[int(G.pop[n]/300.0) for n in G] + nx.draw_networkx_edges(G,G.pos,edge_color=c,width=4,alpha=0.5) + nx.draw_networkx_nodes(G,G.pos,node_size=node_size,node_color=c,alpha=0.5) + nx.draw_networkx_nodes(G,G.pos,node_size=5,node_color='k') + + for c in city: + x,y=city[c] + plt.text(x,y+0.1,c) + plt.savefig("napoleon_russian_campaign.png") + except ImportError: + pass + diff --git a/share/doc/networkx-1.11/examples/graph/roget.py b/share/doc/networkx-1.11/examples/graph/roget.py new file mode 100644 index 000000000..b31baa322 --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/roget.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +""" +Build a directed graph of 1022 categories and +5075 cross-references as defined in the 1879 version of Roget's Thesaurus +contained in the datafile roget_dat.txt. This example is described in +Section 1.2 in Knuth's book [1,2]. + +Note that one of the 5075 cross references is a self loop yet +it is included in the graph built here because +the standard networkx DiGraph class allows self loops. +(cf. 400pungency:400 401 403 405). + +References. +---------- + +[1] Donald E. Knuth, + "The Stanford GraphBase: A Platform for Combinatorial Computing", + ACM Press, New York, 1993. +[2] http://www-cs-faculty.stanford.edu/~knuth/sgb.html + + +""" +from __future__ import print_function +# Authors: Brendt Wohlberg, Aric Hagberg (hagberg@lanl.gov) +# Date: 2005-04-01 07:56:22 -0700 (Fri, 01 Apr 2005) + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +from networkx import * +import re +import sys + +def roget_graph(): + """ Return the thesaurus graph from the roget.dat example in + the Stanford Graph Base. + """ + # open file roget_dat.txt.gz (or roget_dat.txt) + import gzip + fh=gzip.open('roget_dat.txt.gz','r') + + G=DiGraph() + + for line in fh.readlines(): + line = line.decode() + if line.startswith("*"): # skip comments + continue + if line.startswith(" "): # this is a continuation line, append + line=oldline+line + if line.endswith("\\\n"): # continuation line, buffer, goto next + oldline=line.strip("\\\n") + continue + + (headname,tails)=line.split(":") + + # head + numfind=re.compile("^\d+") # re to find the number of this word + head=numfind.findall(headname)[0] # get the number + + G.add_node(head) + + for tail in tails.split(): + if head==tail: + print("skipping self loop",head,tail, file=sys.stderr) + G.add_edge(head,tail) + + return G + +if __name__ == '__main__': + from networkx import * + G=roget_graph() + print("Loaded roget_dat.txt containing 1022 categories.") + print("digraph has %d nodes with %d edges"\ + %(number_of_nodes(G),number_of_edges(G))) + UG=G.to_undirected() + print(number_connected_components(UG),"connected components") + diff --git a/share/doc/networkx-1.11/examples/graph/roget_dat.txt.gz b/share/doc/networkx-1.11/examples/graph/roget_dat.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..6552465dead35f3141227404d8925af0873a643c GIT binary patch literal 15758 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 +Subject: NetworkX +Date: Thu, 16 Jun 2005 16:12:13 -0700 +To: Bob +Status: RO +Content-Length: 86 +Lines: 5 + +Bob, check out the new networkx release - you and +Carol might really like it. + +Alice + + +From bob@gov Thu Jun 16 18:13:12 2005 +Return-Path: +Subject: Re: NetworkX +From: Bob +To: Alice +Content-Type: text/plain +Date: Thu, 16 Jun 2005 18:13:12 -0700 +Status: RO +Content-Length: 26 +Lines: 4 + +Thanks for the tip. + +Bob + + +From ted@com Thu Jul 28 09:53:31 2005 +Return-Path: +Subject: Graph package in Python? +From: Ted +To: Bob +Content-Type: text/plain +Date: Thu, 28 Jul 2005 09:47:03 -0700 +Status: RO +Content-Length: 90 +Lines: 3 + +Hey Ted - I'm looking for a Python package for +graphs and networks. Do you know of any? + + +From bob@gov Thu Jul 28 09:59:31 2005 +Return-Path: +Subject: Re: Graph package in Python? +From: Bob +To: Ted +Content-Type: text/plain +Date: Thu, 28 Jul 2005 09:59:03 -0700 +Status: RO +Content-Length: 180 +Lines: 9 + + +Check out the NetworkX package - Alice sent me the tip! + +Bob + +>> bob@gov scrawled: +>> Hey Ted - I'm looking for a Python package for +>> graphs and networks. Do you know of any? + + +From ted@com Thu Jul 28 15:53:31 2005 +Return-Path: +Subject: get together for lunch to discuss Networks? +From: Ted +To: Bob , Carol , Alice +Content-Type: text/plain +Date: Thu, 28 Jul 2005 15:47:03 -0700 +Status: RO +Content-Length: 139 +Lines: 5 + +Hey everyrone! Want to meet at that restaurant on the +island in Konigsburg tonight? Bring your laptops +and we can install NetworkX. + +Ted + diff --git a/share/doc/networkx-1.11/examples/graph/unix_email.py b/share/doc/networkx-1.11/examples/graph/unix_email.py new file mode 100644 index 000000000..ae95b729a --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/unix_email.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +""" +Create a directed graph, allowing multiple edges and self loops, from +a unix mailbox. The nodes are email addresses with links +that point from the sender to the recievers. The edge data +is a Python email.Message object which contains all of +the email message data. + +This example shows the power of XDiGraph to hold edge data +of arbitrary Python objects (in this case a list of email messages). + +By default, load the sample unix email mailbox called "unix_email.mbox". +You can load your own mailbox by naming it on the command line, eg + +python unixemail.py /var/spool/mail/username + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2005-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import email +from email.utils import getaddresses,parseaddr +import mailbox +import sys + +# unix mailbox recipe +# see http://www.python.org/doc/current/lib/module-mailbox.html +def msgfactory(fp): + try: + return email.message_from_file(fp) + except email.Errors.MessageParseError: + # Don't return None since that will stop the mailbox iterator + return '' + + + +if __name__ == '__main__': + + import networkx as nx + try: + import matplotlib.pyplot as plt + except: + pass + + if len(sys.argv)==1: + filePath = "unix_email.mbox" + else: + filePath = sys.argv[1] + + mbox = mailbox.mbox(filePath, msgfactory) # parse unix mailbox + + G=nx.MultiDiGraph() # create empty graph + + # parse each messages and build graph + for msg in mbox: # msg is python email.Message.Message object + (source_name,source_addr) = parseaddr(msg['From']) # sender + # get all recipients + # see http://www.python.org/doc/current/lib/module-email.Utils.html + tos = msg.get_all('to', []) + ccs = msg.get_all('cc', []) + resent_tos = msg.get_all('resent-to', []) + resent_ccs = msg.get_all('resent-cc', []) + all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs) + # now add the edges for this mail message + for (target_name,target_addr) in all_recipients: + G.add_edge(source_addr,target_addr,message=msg) + + # print edges with message subject + for (u,v,d) in G.edges_iter(data=True): + print("From: %s To: %s Subject: %s"%(u,v,d['message']["Subject"])) + + + try: # draw + pos=nx.spring_layout(G,iterations=10) + nx.draw(G,pos,node_size=0,alpha=0.4,edge_color='r',font_size=16) + plt.savefig("unix_email.png") + plt.show() + except: # matplotlib not available + pass diff --git a/share/doc/networkx-1.11/examples/graph/words.py b/share/doc/networkx-1.11/examples/graph/words.py new file mode 100644 index 000000000..9b5b9dfcf --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/words.py @@ -0,0 +1,84 @@ +""" +Words/Ladder Graph +------------------ +Generate an undirected graph over the 5757 5-letter words in the +datafile words_dat.txt.gz. Two words are connected by an edge +if they differ in one letter, resulting in 14,135 edges. This example +is described in Section 1.1 in Knuth's book [1]_,[2]_. + +References +---------- +.. [1] Donald E. Knuth, + "The Stanford GraphBase: A Platform for Combinatorial Computing", + ACM Press, New York, 1993. +.. [2] http://www-cs-faculty.stanford.edu/~knuth/sgb.html +""" +# Authors: Aric Hagberg (hagberg@lanl.gov), +# Brendt Wohlberg, +# hughdbrown@yahoo.com + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx + +#------------------------------------------------------------------- +# The Words/Ladder graph of Section 1.1 +#------------------------------------------------------------------- +def generate_graph(words): + from string import ascii_lowercase as lowercase + G = nx.Graph(name="words") + lookup = dict((c,lowercase.index(c)) for c in lowercase) + def edit_distance_one(word): + for i in range(len(word)): + left, c, right = word[0:i], word[i], word[i+1:] + j = lookup[c] # lowercase.index(c) + for cc in lowercase[j+1:]: + yield left + cc + right + candgen = ((word, cand) for word in sorted(words) + for cand in edit_distance_one(word) if cand in words) + G.add_nodes_from(words) + for word, cand in candgen: + G.add_edge(word, cand) + return G + +def words_graph(): + """Return the words example graph from the Stanford GraphBase""" + import gzip + fh=gzip.open('words_dat.txt.gz','r') + words=set() + for line in fh.readlines(): + line = line.decode() + if line.startswith('*'): + continue + w=str(line[0:5]) + words.add(w) + return generate_graph(words) + +if __name__ == '__main__': + from networkx import * + G=words_graph() + print("Loaded words_dat.txt containing 5757 five-letter English words.") + print("Two words are connected if they differ in one letter.") + print("Graph has %d nodes with %d edges" + %(number_of_nodes(G),number_of_edges(G))) + print("%d connected components" % number_connected_components(G)) + + for (source,target) in [('chaos','order'), + ('nodes','graph'), + ('pound','marks')]: + print("Shortest path between %s and %s is"%(source,target)) + try: + sp=shortest_path(G, source, target) + for n in sp: + print(n) + except nx.NetworkXNoPath: + print("None") + + + + diff --git a/share/doc/networkx-1.11/examples/graph/words_dat.txt.gz b/share/doc/networkx-1.11/examples/graph/words_dat.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..78aff7913e6048eb7ed30fca14df42b34643ab53 GIT binary patch literal 33695 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 TaVzJ4`}qF=34&oLgB1Y)QF@!O literal 0 HcmV?d00001 diff --git a/share/doc/networkx-1.11/examples/multigraph/chess_masters.py b/share/doc/networkx-1.11/examples/multigraph/chess_masters.py new file mode 100644 index 000000000..76e8ae6f7 --- /dev/null +++ b/share/doc/networkx-1.11/examples/multigraph/chess_masters.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python + +""" +An example of the MultiDiGraph clas + +The function chess_pgn_graph reads a collection of chess +matches stored in the specified PGN file +(PGN ="Portable Game Notation") +Here the (compressed) default file --- + chess_masters_WCC.pgn.bz2 --- +contains all 685 World Chess Championship matches +from 1886 - 1985. +(data from http://chessproblem.my-free-games.com/chess/games/Download-PGN.php) + +The chess_pgn_graph() function returns a MultiDiGraph +with multiple edges. Each node is +the last name of a chess master. Each edge is directed +from white to black and contains selected game info. + +The key statement in chess_pgn_graph below is + G.add_edge(white, black, game_info) +where game_info is a dict describing each game. + +""" +# Copyright (C) 2006-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx + +# tag names specifying what game info should be +# stored in the dict on each digraph edge +game_details=["Event", + "Date", + "Result", + "ECO", + "Site"] + +def chess_pgn_graph(pgn_file="chess_masters_WCC.pgn.bz2"): + """Read chess games in pgn format in pgn_file. + + Filenames ending in .gz or .bz2 will be uncompressed. + + Return the MultiDiGraph of players connected by a chess game. + Edges contain game data in a dict. + + """ + import bz2 + G=nx.MultiDiGraph() + game={} + datafile = bz2.BZ2File(pgn_file) + lines = (line.decode().rstrip('\r\n') for line in datafile) + for line in lines: + if line.startswith('['): + tag,value=line[1:-1].split(' ',1) + game[str(tag)]=value.strip('"') + else: + # empty line after tag set indicates + # we finished reading game info + if game: + white=game.pop('White') + black=game.pop('Black') + G.add_edge(white, black, **game) + game={} + return G + + +if __name__ == '__main__': + G=chess_pgn_graph() + + ngames=G.number_of_edges() + nplayers=G.number_of_nodes() + + print("Loaded %d chess games between %d players\n"\ + % (ngames,nplayers)) + + # identify connected components + # of the undirected version + Gcc=list(nx.connected_component_subgraphs(G.to_undirected())) + if len(Gcc)>1: + print("Note the disconnected component consisting of:") + print(Gcc[1].nodes()) + + # find all games with B97 opening (as described in ECO) + openings=set([game_info['ECO'] + for (white,black,game_info) in G.edges(data=True)]) + print("\nFrom a total of %d different openings,"%len(openings)) + print('the following games used the Sicilian opening') + print('with the Najdorff 7...Qb6 "Poisoned Pawn" variation.\n') + + for (white,black,game_info) in G.edges(data=True): + if game_info['ECO']=='B97': + print(white,"vs",black) + for k,v in game_info.items(): + print(" ",k,": ",v) + print("\n") + + + try: + import matplotlib.pyplot as plt + except ImportError: + import sys + print("Matplotlib needed for drawing. Skipping") + sys.exit(0) + + # make new undirected graph H without multi-edges + H=nx.Graph(G) + + # edge width is proportional number of games played + edgewidth=[] + for (u,v,d) in H.edges(data=True): + edgewidth.append(len(G.get_edge_data(u,v))) + + # node size is proportional to number of games won + wins=dict.fromkeys(G.nodes(),0.0) + for (u,v,d) in G.edges(data=True): + r=d['Result'].split('-') + if r[0]=='1': + wins[u]+=1.0 + elif r[0]=='1/2': + wins[u]+=0.5 + wins[v]+=0.5 + else: + wins[v]+=1.0 + try: + pos=nx.nx_agraph.graphviz_layout(H) + except: + pos=nx.spring_layout(H,iterations=20) + + plt.rcParams['text.usetex'] = False + plt.figure(figsize=(8,8)) + nx.draw_networkx_edges(H,pos,alpha=0.3,width=edgewidth, edge_color='m') + nodesize=[wins[v]*50 for v in H] + nx.draw_networkx_nodes(H,pos,node_size=nodesize,node_color='w',alpha=0.4) + nx.draw_networkx_edges(H,pos,alpha=0.4,node_size=0,width=1,edge_color='k') + nx.draw_networkx_labels(H,pos,fontsize=14) + font = {'fontname' : 'Helvetica', + 'color' : 'k', + 'fontweight' : 'bold', + 'fontsize' : 14} + plt.title("World Chess Championship Games: 1886 - 1985", font) + + # change font and write text (using data coordinates) + font = {'fontname' : 'Helvetica', + 'color' : 'r', + 'fontweight' : 'bold', + 'fontsize' : 14} + + plt.text(0.5, 0.97, "edge width = # games played", + horizontalalignment='center', + transform=plt.gca().transAxes) + plt.text(0.5, 0.94, "node size = # games won", + horizontalalignment='center', + transform=plt.gca().transAxes) + + plt.axis('off') + plt.savefig("chess_masters.png",dpi=75) + print("Wrote chess_masters.png") + plt.show() # display diff --git a/share/doc/networkx-1.11/examples/multigraph/chess_masters_WCC.pgn.bz2 b/share/doc/networkx-1.11/examples/multigraph/chess_masters_WCC.pgn.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..3761ce5280c7a4906b04fab66d422cf92ab567be GIT binary patch literal 100224 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 BrKJD> literal 0 HcmV?d00001 diff --git a/share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_attributes.py b/share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_attributes.py new file mode 100644 index 000000000..0279569ac --- /dev/null +++ b/share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_attributes.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +""" +An example showing how to use the interface to the pygraphviz +AGraph class to convert to and from graphviz. + +Also see the pygraphviz documentation and examples at +http://pygraphviz.github.io/ + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2006-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx + +# networkx graph +G = nx.Graph() +# ad edges with red color +G.add_edge(1, 2, color='red') +G.add_edge(2, 3, color='red') +# add nodes 3 and 4 +G.add_node(3) +G.add_node(4) + +# convert to a graphviz agraph +A = nx.nx_agraph.to_agraph(G) + +# write to dot file +A.write('k5_attributes.dot') + +# convert back to networkx Graph with attributes on edges and +# default attributes as dictionary data +X = nx.nx_agraph.from_agraph(A) +print("edges") +print(X.edges(data=True)) +print("default graph attributes") +print(X.graph) +print("node node attributes") +print(X.node) diff --git a/share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_draw.py b/share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_draw.py new file mode 100644 index 000000000..8f361eb22 --- /dev/null +++ b/share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_draw.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +""" +An example showing how to use the interface to the pygraphviz +AGraph class to draw a graph. + +Also see the pygraphviz documentation and examples at +http://pygraphviz.github.io/ + + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2006-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx + +# plain graph + +G = nx.complete_graph(5) # start with K5 in networkx +A = nx.nx_agraph.to_agraph(G) # convert to a graphviz graph +A.layout() # neato layout +A.draw("k5.ps") # write postscript in k5.ps with neato layout + diff --git a/share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_simple.py b/share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_simple.py new file mode 100644 index 000000000..7fb4ad421 --- /dev/null +++ b/share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_simple.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +""" +An example showing how to use the interface to the pygraphviz +AGraph class to convert to and from graphviz. + +Also see the pygraphviz documentation and examples at +http://pygraphviz.github.io/ + + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2006-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx + +# plain graph + +G = nx.complete_graph(5) # start with K5 in networkx +A = nx.nx_agraph.to_agraph(G) # convert to a graphviz graph +X1 = nx.nx_agraph.from_agraph(A) # convert back to networkx (but as Graph) +X2 = nx.Graph(A) # fancy way to do conversion +G1 = nx.Graph(X1) # now make it a Graph + +A.write('k5.dot') # write to dot file +X3 = nx.nx_agraph.read_dot('k5.dot') # read from dotfile + diff --git a/share/doc/networkx-1.11/examples/pygraphviz/write_dotfile.py b/share/doc/networkx-1.11/examples/pygraphviz/write_dotfile.py new file mode 100644 index 000000000..5d41ff0d5 --- /dev/null +++ b/share/doc/networkx-1.11/examples/pygraphviz/write_dotfile.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +""" +Write a dot file from a networkx graph for further processing with graphviz. + +You need to have either pygraphviz or pydotplus for this example. + +See http://networkx.github.io/documentation/latest/reference/drawing.html +for more info. + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx + +# and the following code block is not needed +# but we want to see which module is used and +# if and why it fails +try: + import pygraphviz + from networkx.drawing.nx_agraph import write_dot + print("using package pygraphviz") +except ImportError: + try: + import pydotplus + from networkx.drawing.nx_pydot import write_dot + print("using package pydotplus") + except ImportError: + print() + print("Both pygraphviz and pydotplus were not found ") + print("see http://networkx.github.io/documentation" + "/latest/reference/drawing.html for info") + print() + raise + +G=nx.grid_2d_graph(5,5) # 5x5 grid +write_dot(G,"grid.dot") +print("Now run: neato -Tps grid.dot >grid.ps") From 742c38f80e7d1ef008ffc3520158ef0f5b9fc8fd Mon Sep 17 00:00:00 2001 From: Chris Seto Date: Mon, 26 Jun 2017 16:22:59 -0400 Subject: [PATCH 18/34] [SHARE-924][Fix] Make RawDataJanitor actually feasible --- share/janitor/tasks.py | 25 ++++++++++++- share/migrations/0040_rawdatum_no_change.py | 21 +++++++++++ share/migrations/0041_no_change_index.py | 22 +++++++++++ share/models/ingest.py | 11 ++++++ share/tasks.py | 7 +++- tests/factories/__init__.py | 3 +- tests/share/tasks/test_transform.py | 41 +++++++++++++++++++++ tests/share/test_janitor.py | 21 +++++++---- 8 files changed, 139 insertions(+), 12 deletions(-) create mode 100644 share/migrations/0040_rawdatum_no_change.py create mode 100644 share/migrations/0041_no_change_index.py create mode 100644 tests/share/tasks/test_transform.py diff --git a/share/janitor/tasks.py b/share/janitor/tasks.py index 9f77e0829..1a0b76e19 100644 --- a/share/janitor/tasks.py +++ b/share/janitor/tasks.py @@ -2,19 +2,40 @@ import celery +from django.db.models import Exists +from django.db.models import OuterRef + from share import tasks from share.models import RawDatum +from share.models import NormalizedData logger = logging.getLogger(__name__) @celery.shared_task(bind=True) -def rawdata_janitor(self, limit=100): +def rawdata_janitor(self, limit=500): """Find RawDatum that do not have a NormalizedData process them """ count = 0 - for rd in RawDatum.objects.select_related('suid__source_config').filter(normalizeddata__isnull=True).order_by('id')[:limit].iterator(): + + # NOTE: Do NOT use .iterator here. It will create a temporary table and eat disk space like no other + # the limit lets this query fit nicely in memory and actually finish executing + # Be very careful about changing this query. If you do change it, make sure the EXPLAIN looks something like this: + # Limit (cost=1.13..Much Smaller Numbers) + # -> Nested Loop (cost=1.13..Big Numbers) + # Join Filter: (share_sourceuniqueidentifier.source_config_id = share_sourceconfig.id) + # -> Nested Loop (cost=1.13..Big Numbers) + # -> Nested Loop Anti Join (cost=0.56..Big Numbers) + # -> Seq Scan on share_rawdatum (cost=0.00..Big Numbers) + # -> Index Only Scan using share_normalizeddata_c0e72696 on share_normalizeddata (cost=0.56..Small Numbers) + # Index Cond: (raw_id = share_rawdatum.id) + + qs = RawDatum.objects.select_related('suid__source_config').annotate( + has_normalizedata=Exists(NormalizedData.objects.values('id').filter(raw=OuterRef('id'))), + ).exclude(no_change=True).exclude(has_normalizedata=True) + + for rd in qs[:limit]: count += 1 logger.debug('Found unprocessed %r from %r', rd, rd.suid.source_config) try: diff --git a/share/migrations/0040_rawdatum_no_change.py b/share/migrations/0040_rawdatum_no_change.py new file mode 100644 index 000000000..f3bc7fbd0 --- /dev/null +++ b/share/migrations/0040_rawdatum_no_change.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.1 on 2017-06-26 20:09 +from __future__ import unicode_literals + +from django.db import migrations, models +import share.models.indexes + + +class Migration(migrations.Migration): + + dependencies = [ + ('share', '0039_auto_20170614_1825'), + ] + + operations = [ + migrations.AddField( + model_name='rawdatum', + name='no_change', + field=models.NullBooleanField(help_text='Indicates that this RawDatum does not contain any new information. This allows the RawDataJanitor to find records that have not been processed.Records that do not contain changes will not have a NormalizedData associated with them, which would otherwise look like data that has not yet been processed.'), + ), + ] diff --git a/share/migrations/0041_no_change_index.py b/share/migrations/0041_no_change_index.py new file mode 100644 index 000000000..2ed98aeac --- /dev/null +++ b/share/migrations/0041_no_change_index.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.1 on 2017-06-26 20:09 +from __future__ import unicode_literals + +from django.db import migrations +import share.models.indexes + + +class Migration(migrations.Migration): + + atomic = False + + dependencies = [ + ('share', '0040_rawdatum_no_change'), + ] + + operations = [ + migrations.AddIndex( + model_name='rawdatum', + index=share.models.indexes.ConcurrentIndex(fields=['no_change'], name='share_rawda_no_chan_15308a_idx'), + ), + ] diff --git a/share/models/ingest.py b/share/models/ingest.py index a984f65b4..5614c0396 100644 --- a/share/models/ingest.py +++ b/share/models/ingest.py @@ -17,6 +17,7 @@ from django.utils.deconstruct import deconstructible from share.models.fuzzycount import FuzzyCountManager +from share.models.indexes import ConcurrentIndex from share.util import chunked, placeholders @@ -380,6 +381,13 @@ class RawDatum(models.Model): date_modified = models.DateTimeField(auto_now=True, editable=False) date_created = models.DateTimeField(auto_now_add=True, editable=False) + no_change = models.NullBooleanField(null=True, help_text=( + 'Indicates that this RawDatum does not contain any new information. ' + 'This allows the RawDataJanitor to find records that have not been processed.' + 'Records that do not contain changes will not have a NormalizedData associated with them, ' + 'which would otherwise look like data that has not yet been processed.' + )) + logs = models.ManyToManyField('HarvestLog', related_name='raw_data') objects = RawDatumManager() @@ -391,6 +399,9 @@ def created(self): class Meta: unique_together = ('suid', 'sha256') verbose_name_plural = 'Raw Data' + indexes = [ + ConcurrentIndex(fields=['no_change']) + ] class JSONAPIMeta: resource_name = 'RawData' diff --git a/share/tasks.py b/share/tasks.py index 06416b9ba..ef8e904e9 100644 --- a/share/tasks.py +++ b/share/tasks.py @@ -44,7 +44,12 @@ def transform(self, raw_id): graph = transformer.transform(raw) if not graph or not graph['@graph']: - logger.warning('Graph was empty for %s, skipping...', raw) + if not raw.normalizeddata_set.exists(): + logger.warning('Graph was empty for %s, setting no_change to True', raw) + RawDatum.objects.filter(id=raw_id).update(no_change=True) + else: + logger.warning('Graph was empty for %s, but a normalized data already exists for it', raw) + return except Exception as e: logger.exception('Failed to transform %r', raw) diff --git a/tests/factories/__init__.py b/tests/factories/__init__.py index 56e5c6495..6b08e43ad 100644 --- a/tests/factories/__init__.py +++ b/tests/factories/__init__.py @@ -1,6 +1,7 @@ from unittest import mock import datetime import hashlib +import json import pkg_resources import uuid @@ -92,7 +93,7 @@ class MockTransformer(BaseTransformer): VERSION = 1 def do_transform(self, data): - raise NotImplementedError('Transformers must implement do_transform') + return json.loads(data), None mock_entry = mock.create_autospec(pkg_resources.EntryPoint, instance=True) mock_entry.name = self.key diff --git a/tests/share/tasks/test_transform.py b/tests/share/tasks/test_transform.py new file mode 100644 index 000000000..0ee3f5a84 --- /dev/null +++ b/tests/share/tasks/test_transform.py @@ -0,0 +1,41 @@ +import pytest +import json +from unittest import mock + +from share import tasks + +from tests import factories + + +@pytest.mark.django_db +class TestTransform: + + @pytest.fixture(autouse=True) + def mock_requests(self, monkeypatch): + mrequests = mock.Mock() + monkeypatch.setattr('share.tasks.requests', mrequests) + return mrequests + + def test_set_no_change(self): + raw = factories.RawDatumFactory(datum=json.dumps({ + '@graph': [] + })) + + tasks.transform(raw.id) + + raw.refresh_from_db() + + assert raw.no_change is True + + def test_does_not_set_no_change(self): + raw = factories.RawDatumFactory(datum=json.dumps({ + '@graph': [] + })) + + factories.NormalizedDataFactory(raw=raw) + + tasks.transform(raw.id) + + raw.refresh_from_db() + + assert raw.no_change is None diff --git a/tests/share/test_janitor.py b/tests/share/test_janitor.py index f04fc77d2..af74ad031 100644 --- a/tests/share/test_janitor.py +++ b/tests/share/test_janitor.py @@ -23,10 +23,9 @@ def test_empty(self, mock_transform): def test_unprocessed_data(self, mock_transform): rds = factories.RawDatumFactory.create_batch(55) assert rawdata_janitor() == 55 - assert mock_transform.call_args_list == [ - mock.call((rd.id,), throw=True, retries=4) - for rd in sorted(rds, key=lambda r: r.id) - ] + assert sorted(mock_transform.call_args_list) == sorted([ + mock.call((rd.id,), throw=True, retries=4) for rd in rds + ]) def test_idempotent(self, mock_transform): rds = factories.RawDatumFactory.create_batch(55) @@ -46,7 +45,13 @@ def test_some_unprocessed_date(self, mock_transform): assert rawdata_janitor() == 30 - assert mock_transform.call_args_list == [ - mock.call((rd.id,), throw=True, retries=4) - for rd in sorted(rds[25:], key=lambda r: r.id) - ] + assert sorted(mock_transform.call_args_list) == sorted([ + mock.call((rd.id,), throw=True, retries=4) for rd in rds[25:] + ]) + + def test_ignores_no_change(self, mock_transform): + factories.RawDatumFactory.create_batch(55, no_change=True) + + assert rawdata_janitor() == 0 + + assert mock_transform.call_args_list == [] From 8f1119939e1c0b7ff8afade8f614916763f390c7 Mon Sep 17 00:00:00 2001 From: Chris Seto Date: Tue, 27 Jun 2017 13:20:48 -0400 Subject: [PATCH 19/34] [Fix] Correct harvester scheduling --- share/harvest/scheduler.py | 4 ++-- tests/factories/__init__.py | 6 ++++-- tests/share/test_harvest.py | 42 +++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/share/harvest/scheduler.py b/share/harvest/scheduler.py index 548675e22..dcf8abf96 100644 --- a/share/harvest/scheduler.py +++ b/share/harvest/scheduler.py @@ -34,12 +34,12 @@ def all(self, cutoff=None, allow_full_harvest=True, **kwargs): if hasattr(self.source_config, 'latest'): latest_date = self.source_config.latest else: - latest_date = HarvestLog.objects.filter(source_config=self.source_config).aggregate(models.Max('end_date')) + latest_date = HarvestLog.objects.filter(source_config=self.source_config).aggregate(models.Max('end_date'))['end_date__max'] # If we can build full harvests and the earliest log that would be generated does NOT exist # Go ahead and reset the latest_date to the earliest_date if allow_full_harvest and self.source_config.earliest_date and self.source_config.full_harvest: - if not HarvestLog.objects.filter(start_date=self.source_config.earliest_date).exists(): + if not self.source_config.harvest_logs.filter(start_date=self.source_config.earliest_date).exists(): latest_date = self.source_config.earliest_date # If nothing sets latest_date, default to the soonest possible harvest diff --git a/tests/factories/__init__.py b/tests/factories/__init__.py index 6b08e43ad..d3ab16c2f 100644 --- a/tests/factories/__init__.py +++ b/tests/factories/__init__.py @@ -126,8 +126,10 @@ class Meta: def _generate(cls, create, attrs): attrs['source_config_version'] = attrs['source_config'].version attrs['harvester_version'] = attrs['source_config'].harvester.version - attrs['start_date'] = datetime.datetime.combine(attrs['start_date'].date(), datetime.time(0, 0, 0, 0, timezone.utc)) - attrs['end_date'] = attrs['start_date'] + datetime.timedelta(days=1) + if isinstance(attrs['start_date'], datetime.datetime): + attrs['start_date'] = attrs['start_date'].date() + if not attrs.get('end_date'): + attrs['end_date'] = attrs['start_date'] + datetime.timedelta(days=1) return super()._generate(create, attrs) diff --git a/tests/share/test_harvest.py b/tests/share/test_harvest.py index c57e32a51..23392054f 100644 --- a/tests/share/test_harvest.py +++ b/tests/share/test_harvest.py @@ -401,3 +401,45 @@ def test_harvest_after(self, monkeypatch, now, end_date, harvest_after, should_r tasks.harvest() assert source_config.harvester.get_class()._do_fetch.called == should_run + + def test_latest_date(self): + source_config = factories.SourceConfigFactory( + full_harvest=True, + earliest_date=pendulum.parse('2017-01-01').date() + ) + + # We have a harvest log with start_date equal to earliest_date + # but a different source_config + factories.HarvestLogFactory( + start_date=pendulum.parse('2017-01-01').date(), + end_date=pendulum.parse('2017-01-02').date(), + ) + + assert len(HarvestScheduler(source_config).all(cutoff=pendulum.parse('2018-01-01').date())) == 365 + + def test_caught_up(self): + source_config = factories.SourceConfigFactory( + full_harvest=True, + earliest_date=pendulum.parse('2017-01-01').date() + ) + + factories.HarvestLogFactory( + source_config=source_config, + start_date=pendulum.parse('2017-01-01').date(), + end_date=pendulum.parse('2017-01-02').date(), + ) + + factories.HarvestLogFactory( + source_config=source_config, + start_date=pendulum.parse('2018-01-01').date(), + end_date=pendulum.parse('2018-01-02').date(), + ) + + assert len(HarvestScheduler(source_config).all(cutoff=pendulum.parse('2018-01-01').date())) == 0 + + def test_latest_date_null(self): + source_config = factories.SourceConfigFactory( + full_harvest=True, + earliest_date=pendulum.parse('2017-01-01').date() + ) + assert len(HarvestScheduler(source_config).all(cutoff=pendulum.parse('2018-01-01').date())) == 365 From bd013f2a00732a19e00f174db8f956884aa0b3b3 Mon Sep 17 00:00:00 2001 From: Chris Seto Date: Tue, 27 Jun 2017 13:28:53 -0400 Subject: [PATCH 20/34] Update naming --- share/migrations/0040_rawdatum_no_change.py | 4 ++-- share/migrations/0041_no_change_index.py | 2 +- share/models/ingest.py | 14 +++++++------- share/tasks.py | 4 ++-- tests/factories/__init__.py | 2 -- tests/share/tasks/test_transform.py | 8 ++++---- tests/share/test_janitor.py | 4 ++-- 7 files changed, 18 insertions(+), 20 deletions(-) diff --git a/share/migrations/0040_rawdatum_no_change.py b/share/migrations/0040_rawdatum_no_change.py index f3bc7fbd0..49044faf2 100644 --- a/share/migrations/0040_rawdatum_no_change.py +++ b/share/migrations/0040_rawdatum_no_change.py @@ -15,7 +15,7 @@ class Migration(migrations.Migration): operations = [ migrations.AddField( model_name='rawdatum', - name='no_change', - field=models.NullBooleanField(help_text='Indicates that this RawDatum does not contain any new information. This allows the RawDataJanitor to find records that have not been processed.Records that do not contain changes will not have a NormalizedData associated with them, which would otherwise look like data that has not yet been processed.'), + name='no_output', + field=models.NullBooleanField(help_text='Indicates that this RawDatum resulted in an empty graph when transformed. This allows the RawDataJanitor to find records that have not been processed. Records that result in an empty graph will not have a NormalizedData associated with them, which would otherwise look like data that has not yet been processed.'), ), ] diff --git a/share/migrations/0041_no_change_index.py b/share/migrations/0041_no_change_index.py index 2ed98aeac..b2d3f98c9 100644 --- a/share/migrations/0041_no_change_index.py +++ b/share/migrations/0041_no_change_index.py @@ -17,6 +17,6 @@ class Migration(migrations.Migration): operations = [ migrations.AddIndex( model_name='rawdatum', - index=share.models.indexes.ConcurrentIndex(fields=['no_change'], name='share_rawda_no_chan_15308a_idx'), + index=share.models.indexes.ConcurrentIndex(fields=['no_output'], name='share_rawda_no_outp_f0330f_idx'), ), ] diff --git a/share/models/ingest.py b/share/models/ingest.py index 5614c0396..bb0472d26 100644 --- a/share/models/ingest.py +++ b/share/models/ingest.py @@ -381,10 +381,10 @@ class RawDatum(models.Model): date_modified = models.DateTimeField(auto_now=True, editable=False) date_created = models.DateTimeField(auto_now_add=True, editable=False) - no_change = models.NullBooleanField(null=True, help_text=( - 'Indicates that this RawDatum does not contain any new information. ' - 'This allows the RawDataJanitor to find records that have not been processed.' - 'Records that do not contain changes will not have a NormalizedData associated with them, ' + no_output = models.NullBooleanField(null=True, help_text=( + 'Indicates that this RawDatum resulted in an empty graph when transformed. ' + 'This allows the RawDataJanitor to find records that have not been processed. ' + 'Records that result in an empty graph will not have a NormalizedData associated with them, ' 'which would otherwise look like data that has not yet been processed.' )) @@ -399,9 +399,9 @@ def created(self): class Meta: unique_together = ('suid', 'sha256') verbose_name_plural = 'Raw Data' - indexes = [ - ConcurrentIndex(fields=['no_change']) - ] + indexes = ( + ConcurrentIndex(fields=['no_output']), + ) class JSONAPIMeta: resource_name = 'RawData' diff --git a/share/tasks.py b/share/tasks.py index ef8e904e9..52ff216d0 100644 --- a/share/tasks.py +++ b/share/tasks.py @@ -45,8 +45,8 @@ def transform(self, raw_id): if not graph or not graph['@graph']: if not raw.normalizeddata_set.exists(): - logger.warning('Graph was empty for %s, setting no_change to True', raw) - RawDatum.objects.filter(id=raw_id).update(no_change=True) + logger.warning('Graph was empty for %s, setting no_output to True', raw) + RawDatum.objects.filter(id=raw_id).update(no_output=True) else: logger.warning('Graph was empty for %s, but a normalized data already exists for it', raw) diff --git a/tests/factories/__init__.py b/tests/factories/__init__.py index d3ab16c2f..be6aad2ce 100644 --- a/tests/factories/__init__.py +++ b/tests/factories/__init__.py @@ -11,8 +11,6 @@ from factory import fuzzy from factory.django import DjangoModelFactory -from django.utils import timezone - from project import celery_app from share import models diff --git a/tests/share/tasks/test_transform.py b/tests/share/tasks/test_transform.py index 0ee3f5a84..99ff1b84d 100644 --- a/tests/share/tasks/test_transform.py +++ b/tests/share/tasks/test_transform.py @@ -16,7 +16,7 @@ def mock_requests(self, monkeypatch): monkeypatch.setattr('share.tasks.requests', mrequests) return mrequests - def test_set_no_change(self): + def test_set_no_output(self): raw = factories.RawDatumFactory(datum=json.dumps({ '@graph': [] })) @@ -25,9 +25,9 @@ def test_set_no_change(self): raw.refresh_from_db() - assert raw.no_change is True + assert raw.no_output is True - def test_does_not_set_no_change(self): + def test_does_not_set_no_output(self): raw = factories.RawDatumFactory(datum=json.dumps({ '@graph': [] })) @@ -38,4 +38,4 @@ def test_does_not_set_no_change(self): raw.refresh_from_db() - assert raw.no_change is None + assert raw.no_output is None diff --git a/tests/share/test_janitor.py b/tests/share/test_janitor.py index af74ad031..6aa974a1e 100644 --- a/tests/share/test_janitor.py +++ b/tests/share/test_janitor.py @@ -49,8 +49,8 @@ def test_some_unprocessed_date(self, mock_transform): mock.call((rd.id,), throw=True, retries=4) for rd in rds[25:] ]) - def test_ignores_no_change(self, mock_transform): - factories.RawDatumFactory.create_batch(55, no_change=True) + def test_ignores_no_output(self, mock_transform): + factories.RawDatumFactory.create_batch(55, no_output=True) assert rawdata_janitor() == 0 From 7727149aa4893d9375f41cacab008258cf4ae92a Mon Sep 17 00:00:00 2001 From: Chris Seto Date: Tue, 27 Jun 2017 13:35:07 -0400 Subject: [PATCH 21/34] Finish renaming --- share/celery.py | 2 +- share/janitor/tasks.py | 2 +- .../{0040_rawdatum_no_change.py => 0040_rawdatum_no_output.py} | 0 share/migrations/0041_no_change_index.py | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename share/migrations/{0040_rawdatum_no_change.py => 0040_rawdatum_no_output.py} (100%) diff --git a/share/celery.py b/share/celery.py index 98052bb27..957c7a3fd 100644 --- a/share/celery.py +++ b/share/celery.py @@ -234,7 +234,7 @@ def delete_queryset(self, queryset): try: with transaction.atomic(): # .delete loads the entire queryset and can't be sliced... Hooray - for ids in chunked(queryset.values_list('id', flat=True), size=self.chunk_size): + for ids in chunked(queryset.values_list('id', flat=True).iterator(), size=self.chunk_size): num_deleted, _ = queryset.model.objects.filter(id__in=ids).delete() total_deleted += num_deleted except Exception as e: diff --git a/share/janitor/tasks.py b/share/janitor/tasks.py index 1a0b76e19..b7f6da272 100644 --- a/share/janitor/tasks.py +++ b/share/janitor/tasks.py @@ -33,7 +33,7 @@ def rawdata_janitor(self, limit=500): qs = RawDatum.objects.select_related('suid__source_config').annotate( has_normalizedata=Exists(NormalizedData.objects.values('id').filter(raw=OuterRef('id'))), - ).exclude(no_change=True).exclude(has_normalizedata=True) + ).exclude(no_output=True).exclude(has_normalizedata=True) for rd in qs[:limit]: count += 1 diff --git a/share/migrations/0040_rawdatum_no_change.py b/share/migrations/0040_rawdatum_no_output.py similarity index 100% rename from share/migrations/0040_rawdatum_no_change.py rename to share/migrations/0040_rawdatum_no_output.py diff --git a/share/migrations/0041_no_change_index.py b/share/migrations/0041_no_change_index.py index b2d3f98c9..a88b6e6ad 100644 --- a/share/migrations/0041_no_change_index.py +++ b/share/migrations/0041_no_change_index.py @@ -11,7 +11,7 @@ class Migration(migrations.Migration): atomic = False dependencies = [ - ('share', '0040_rawdatum_no_change'), + ('share', '0040_rawdatum_no_output'), ] operations = [ From 8de943744451a87ffd9722c4eecd2beeb10623bb Mon Sep 17 00:00:00 2001 From: Oludare Olugbemi Date: Wed, 5 Jul 2017 11:35:53 -0400 Subject: [PATCH 22/34] Commit --- bin/activate | 76 + bin/activate.csh | 37 + bin/activate.fish | 75 + bin/celery | 12 + bin/createfontdatachunk.py | 16 + bin/django-admin | 11 + bin/django-admin.py | 5 + bin/easy_install | 11 + bin/easy_install-3.5 | 11 + bin/enhancer.py | 63 + bin/explode.py | 112 ++ bin/gifmaker.py | 31 + bin/jp.py | 54 + bin/jsonschema | 11 + bin/markdown_py | 34 + bin/newrelic-admin | 12 + bin/painter.py | 82 + bin/pbr | 11 + bin/pilconvert.py | 99 ++ bin/pildriver.py | 526 +++++++ bin/pilfile.py | 101 ++ bin/pilfont.py | 57 + bin/pilprint.py | 102 ++ bin/pip | 11 + bin/pip3 | 11 + bin/pip3.5 | 11 + bin/player.py | 102 ++ bin/python | 1 + bin/python3 | 1 + bin/python3.5 | 1 + bin/raven | 11 + bin/rst2html.py | 23 + bin/rst2html5.py | 35 + bin/rst2latex.py | 26 + bin/rst2man.py | 26 + bin/rst2odt.py | 30 + bin/rst2odt_prepstyles.py | 67 + bin/rst2pseudoxml.py | 23 + bin/rst2s5.py | 24 + bin/rst2xetex.py | 27 + bin/rst2xml.py | 23 + bin/rstpep2html.py | 25 + bin/sharectl | 12 + bin/thresholder.py | 78 + bin/viewer.py | 54 + include/site/python3.5/greenlet/greenlet.h | 148 ++ pip-selfcheck.json | 1 + pyvenv.cfg | 3 + share/doc/networkx-1.11/INSTALL.txt | 3 + share/doc/networkx-1.11/LICENSE.txt | 40 + .../examples/3d_drawing/mayavi2_spring.py | 37 + .../examples/advanced/eigenvalues.py | 20 + .../examples/advanced/heavy_metal_umlaut.py | 77 + .../advanced/iterated_dynamical_systems.py | 199 +++ .../examples/advanced/parallel_betweenness.py | 73 + .../examples/algorithms/blockmodel.py | 83 + .../examples/algorithms/davis_club.py | 36 + .../algorithms/hartford_drug.edgelist | 338 ++++ .../algorithms/krackhardt_centrality.py | 33 + .../networkx-1.11/examples/algorithms/rcm.py | 32 + .../examples/basic/properties.py | 48 + .../examples/basic/read_write.py | 25 + .../networkx-1.11/examples/drawing/atlas.py | 89 ++ .../examples/drawing/chess_masters.py | 162 ++ .../drawing/chess_masters_WCC.pgn.bz2 | Bin 0 -> 100224 bytes .../examples/drawing/circular_tree.py | 22 + .../examples/drawing/degree_histogram.py | 31 + .../examples/drawing/edge_colormap.py | 19 + .../examples/drawing/ego_graph.py | 30 + .../examples/drawing/four_grids.py | 40 + .../examples/drawing/giant_component.py | 79 + .../examples/drawing/house_with_colors.py | 27 + .../examples/drawing/knuth_miles.py | 112 ++ .../examples/drawing/knuth_miles.txt.gz | Bin 0 -> 20317 bytes .../examples/drawing/labels_and_colors.py | 51 + .../examples/drawing/lanl_routes.edgelist | 1363 +++++++++++++++++ .../examples/drawing/lanl_routes.py | 80 + .../examples/drawing/node_colormap.py | 19 + .../drawing/random_geometric_graph.py | 33 + .../networkx-1.11/examples/drawing/sampson.py | 46 + .../examples/drawing/simple_path.py | 16 + .../examples/drawing/unix_email.mbox | 84 + .../examples/drawing/unix_email.py | 85 + .../examples/drawing/weighted_graph.py | 41 + .../doc/networkx-1.11/examples/graph/atlas.py | 89 ++ .../networkx-1.11/examples/graph/atlas2.py | 33 + .../examples/graph/degree_sequence.py | 34 + .../examples/graph/erdos_renyi.py | 38 + .../graph/expected_degree_sequence.py | 29 + .../networkx-1.11/examples/graph/football.py | 46 + .../examples/graph/karate_club.py | 17 + .../examples/graph/knuth_miles.py | 112 ++ .../examples/graph/knuth_miles.txt.gz | Bin 0 -> 20317 bytes .../graph/napoleon_russian_campaign.py | 145 ++ .../doc/networkx-1.11/examples/graph/roget.py | 81 + .../examples/graph/roget_dat.txt.gz | Bin 0 -> 15758 bytes .../examples/graph/unix_email.mbox | 84 + .../examples/graph/unix_email.py | 85 + .../doc/networkx-1.11/examples/graph/words.py | 84 + .../examples/graph/words_dat.txt.gz | Bin 0 -> 33695 bytes .../examples/multigraph/chess_masters.py | 162 ++ .../multigraph/chess_masters_WCC.pgn.bz2 | Bin 0 -> 100224 bytes .../pygraphviz/pygraphviz_attributes.py | 44 + .../examples/pygraphviz/pygraphviz_draw.py | 28 + .../examples/pygraphviz/pygraphviz_simple.py | 32 + .../examples/pygraphviz/write_dotfile.py | 44 + 106 files changed, 6983 insertions(+) create mode 100644 bin/activate create mode 100644 bin/activate.csh create mode 100644 bin/activate.fish create mode 100755 bin/celery create mode 100755 bin/createfontdatachunk.py create mode 100755 bin/django-admin create mode 100755 bin/django-admin.py create mode 100755 bin/easy_install create mode 100755 bin/easy_install-3.5 create mode 100755 bin/enhancer.py create mode 100755 bin/explode.py create mode 100755 bin/gifmaker.py create mode 100755 bin/jp.py create mode 100755 bin/jsonschema create mode 100755 bin/markdown_py create mode 100755 bin/newrelic-admin create mode 100755 bin/painter.py create mode 100755 bin/pbr create mode 100755 bin/pilconvert.py create mode 100755 bin/pildriver.py create mode 100755 bin/pilfile.py create mode 100755 bin/pilfont.py create mode 100755 bin/pilprint.py create mode 100755 bin/pip create mode 100755 bin/pip3 create mode 100755 bin/pip3.5 create mode 100755 bin/player.py create mode 120000 bin/python create mode 120000 bin/python3 create mode 120000 bin/python3.5 create mode 100755 bin/raven create mode 100755 bin/rst2html.py create mode 100755 bin/rst2html5.py create mode 100755 bin/rst2latex.py create mode 100755 bin/rst2man.py create mode 100755 bin/rst2odt.py create mode 100755 bin/rst2odt_prepstyles.py create mode 100755 bin/rst2pseudoxml.py create mode 100755 bin/rst2s5.py create mode 100755 bin/rst2xetex.py create mode 100755 bin/rst2xml.py create mode 100755 bin/rstpep2html.py create mode 100755 bin/sharectl create mode 100755 bin/thresholder.py create mode 100755 bin/viewer.py create mode 100644 include/site/python3.5/greenlet/greenlet.h create mode 100644 pip-selfcheck.json create mode 100644 pyvenv.cfg create mode 100644 share/doc/networkx-1.11/INSTALL.txt create mode 100644 share/doc/networkx-1.11/LICENSE.txt create mode 100644 share/doc/networkx-1.11/examples/3d_drawing/mayavi2_spring.py create mode 100644 share/doc/networkx-1.11/examples/advanced/eigenvalues.py create mode 100644 share/doc/networkx-1.11/examples/advanced/heavy_metal_umlaut.py create mode 100644 share/doc/networkx-1.11/examples/advanced/iterated_dynamical_systems.py create mode 100644 share/doc/networkx-1.11/examples/advanced/parallel_betweenness.py create mode 100644 share/doc/networkx-1.11/examples/algorithms/blockmodel.py create mode 100644 share/doc/networkx-1.11/examples/algorithms/davis_club.py create mode 100644 share/doc/networkx-1.11/examples/algorithms/hartford_drug.edgelist create mode 100644 share/doc/networkx-1.11/examples/algorithms/krackhardt_centrality.py create mode 100644 share/doc/networkx-1.11/examples/algorithms/rcm.py create mode 100644 share/doc/networkx-1.11/examples/basic/properties.py create mode 100644 share/doc/networkx-1.11/examples/basic/read_write.py create mode 100644 share/doc/networkx-1.11/examples/drawing/atlas.py create mode 100644 share/doc/networkx-1.11/examples/drawing/chess_masters.py create mode 100644 share/doc/networkx-1.11/examples/drawing/chess_masters_WCC.pgn.bz2 create mode 100644 share/doc/networkx-1.11/examples/drawing/circular_tree.py create mode 100644 share/doc/networkx-1.11/examples/drawing/degree_histogram.py create mode 100644 share/doc/networkx-1.11/examples/drawing/edge_colormap.py create mode 100644 share/doc/networkx-1.11/examples/drawing/ego_graph.py create mode 100644 share/doc/networkx-1.11/examples/drawing/four_grids.py create mode 100644 share/doc/networkx-1.11/examples/drawing/giant_component.py create mode 100644 share/doc/networkx-1.11/examples/drawing/house_with_colors.py create mode 100644 share/doc/networkx-1.11/examples/drawing/knuth_miles.py create mode 100644 share/doc/networkx-1.11/examples/drawing/knuth_miles.txt.gz create mode 100644 share/doc/networkx-1.11/examples/drawing/labels_and_colors.py create mode 100644 share/doc/networkx-1.11/examples/drawing/lanl_routes.edgelist create mode 100644 share/doc/networkx-1.11/examples/drawing/lanl_routes.py create mode 100644 share/doc/networkx-1.11/examples/drawing/node_colormap.py create mode 100644 share/doc/networkx-1.11/examples/drawing/random_geometric_graph.py create mode 100644 share/doc/networkx-1.11/examples/drawing/sampson.py create mode 100644 share/doc/networkx-1.11/examples/drawing/simple_path.py create mode 100644 share/doc/networkx-1.11/examples/drawing/unix_email.mbox create mode 100755 share/doc/networkx-1.11/examples/drawing/unix_email.py create mode 100644 share/doc/networkx-1.11/examples/drawing/weighted_graph.py create mode 100644 share/doc/networkx-1.11/examples/graph/atlas.py create mode 100644 share/doc/networkx-1.11/examples/graph/atlas2.py create mode 100644 share/doc/networkx-1.11/examples/graph/degree_sequence.py create mode 100644 share/doc/networkx-1.11/examples/graph/erdos_renyi.py create mode 100644 share/doc/networkx-1.11/examples/graph/expected_degree_sequence.py create mode 100644 share/doc/networkx-1.11/examples/graph/football.py create mode 100644 share/doc/networkx-1.11/examples/graph/karate_club.py create mode 100644 share/doc/networkx-1.11/examples/graph/knuth_miles.py create mode 100644 share/doc/networkx-1.11/examples/graph/knuth_miles.txt.gz create mode 100644 share/doc/networkx-1.11/examples/graph/napoleon_russian_campaign.py create mode 100644 share/doc/networkx-1.11/examples/graph/roget.py create mode 100644 share/doc/networkx-1.11/examples/graph/roget_dat.txt.gz create mode 100644 share/doc/networkx-1.11/examples/graph/unix_email.mbox create mode 100644 share/doc/networkx-1.11/examples/graph/unix_email.py create mode 100644 share/doc/networkx-1.11/examples/graph/words.py create mode 100644 share/doc/networkx-1.11/examples/graph/words_dat.txt.gz create mode 100644 share/doc/networkx-1.11/examples/multigraph/chess_masters.py create mode 100644 share/doc/networkx-1.11/examples/multigraph/chess_masters_WCC.pgn.bz2 create mode 100644 share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_attributes.py create mode 100644 share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_draw.py create mode 100644 share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_simple.py create mode 100644 share/doc/networkx-1.11/examples/pygraphviz/write_dotfile.py diff --git a/bin/activate b/bin/activate new file mode 100644 index 000000000..dc65d1cfa --- /dev/null +++ b/bin/activate @@ -0,0 +1,76 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "$_OLD_VIRTUAL_PATH" ] ; then + PATH="$_OLD_VIRTUAL_PATH" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "$_OLD_VIRTUAL_PYTHONHOME" ] ; then + PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then + hash -r + fi + + if [ -n "$_OLD_VIRTUAL_PS1" ] ; then + PS1="$_OLD_VIRTUAL_PS1" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + if [ ! "$1" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="/Users/admin/share" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "$PYTHONHOME" ] ; then + _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME" + unset PYTHONHOME +fi + +if [ -z "$VIRTUAL_ENV_DISABLE_PROMPT" ] ; then + _OLD_VIRTUAL_PS1="$PS1" + if [ "x(share) " != x ] ; then + PS1="(share) $PS1" + else + if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1" + else + PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1" + fi + fi + export PS1 +fi + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then + hash -r +fi diff --git a/bin/activate.csh b/bin/activate.csh new file mode 100644 index 000000000..99fba4aa6 --- /dev/null +++ b/bin/activate.csh @@ -0,0 +1,37 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/Users/admin/share" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + if ("share" != "") then + set env_name = "share" + else + if (`basename "VIRTUAL_ENV"` == "__") then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + set env_name = `basename \`dirname "$VIRTUAL_ENV"\`` + else + set env_name = `basename "$VIRTUAL_ENV"` + endif + endif + set prompt = "[$env_name] $prompt" + unset env_name +endif + +alias pydoc python -m pydoc + +rehash diff --git a/bin/activate.fish b/bin/activate.fish new file mode 100644 index 000000000..4bddb2198 --- /dev/null +++ b/bin/activate.fish @@ -0,0 +1,75 @@ +# This file must be used with ". bin/activate.fish" *from fish* (http://fishshell.org) +# you cannot run it directly + +function deactivate -d "Exit virtualenv and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + functions -e fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + + set -e VIRTUAL_ENV + if test "$argv[1]" != "nondestructive" + # Self destruct! + functions -e deactivate + end +end + +# unset irrelevant variables +deactivate nondestructive + +set -gx VIRTUAL_ENV "/Users/admin/share" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# unset PYTHONHOME if set +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # save the current fish_prompt function as the function _old_fish_prompt + functions -c fish_prompt _old_fish_prompt + + # with the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command + set -l old_status $status + + # Prompt override? + if test -n "(share) " + printf "%s%s" "(share) " (set_color normal) + else + # ...Otherwise, prepend env + set -l _checkbase (basename "$VIRTUAL_ENV") + if test $_checkbase = "__" + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + printf "%s[%s]%s " (set_color -b blue white) (basename (dirname "$VIRTUAL_ENV")) (set_color normal) + else + printf "%s(%s)%s" (set_color -b blue white) (basename "$VIRTUAL_ENV") (set_color normal) + end + end + + # Restore the return status of the previous command. + echo "exit $old_status" | . + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end diff --git a/bin/celery b/bin/celery new file mode 100755 index 000000000..96c8eea01 --- /dev/null +++ b/bin/celery @@ -0,0 +1,12 @@ +#!/Users/admin/SHARE/bin/python3.5 +# EASY-INSTALL-ENTRY-SCRIPT: 'celery==4.0.2','console_scripts','celery' +__requires__ = 'celery==4.0.2' +import re +import sys +from pkg_resources import load_entry_point + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit( + load_entry_point('celery==4.0.2', 'console_scripts', 'celery')() + ) diff --git a/bin/createfontdatachunk.py b/bin/createfontdatachunk.py new file mode 100755 index 000000000..ea664f23c --- /dev/null +++ b/bin/createfontdatachunk.py @@ -0,0 +1,16 @@ +#!/Users/admin/SHARE/bin/python3.5 +from __future__ import print_function +import base64 +import os +import sys + +if __name__ == "__main__": + # create font data chunk for embedding + font = "Tests/images/courB08" + print(" f._load_pilfont_data(") + print(" # %s" % os.path.basename(font)) + print(" BytesIO(base64.decodestring(b'''") + base64.encode(open(font + ".pil", "rb"), sys.stdout) + print("''')), Image.open(BytesIO(base64.decodestring(b'''") + base64.encode(open(font + ".pbm", "rb"), sys.stdout) + print("'''))))") diff --git a/bin/django-admin b/bin/django-admin new file mode 100755 index 000000000..19bc5157b --- /dev/null +++ b/bin/django-admin @@ -0,0 +1,11 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from django.core.management import execute_from_command_line + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(execute_from_command_line()) diff --git a/bin/django-admin.py b/bin/django-admin.py new file mode 100755 index 000000000..dfcc1386f --- /dev/null +++ b/bin/django-admin.py @@ -0,0 +1,5 @@ +#!/Users/admin/SHARE/bin/python3.5 +from django.core import management + +if __name__ == "__main__": + management.execute_from_command_line() diff --git a/bin/easy_install b/bin/easy_install new file mode 100755 index 000000000..be946e242 --- /dev/null +++ b/bin/easy_install @@ -0,0 +1,11 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from setuptools.command.easy_install import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bin/easy_install-3.5 b/bin/easy_install-3.5 new file mode 100755 index 000000000..be946e242 --- /dev/null +++ b/bin/easy_install-3.5 @@ -0,0 +1,11 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from setuptools.command.easy_install import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bin/enhancer.py b/bin/enhancer.py new file mode 100755 index 000000000..8f9e4f76c --- /dev/null +++ b/bin/enhancer.py @@ -0,0 +1,63 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library +# $Id$ +# +# this demo script creates four windows containing an image and a slider. +# drag the slider to modify the image. +# + +try: + from tkinter import Tk, Toplevel, Frame, Label, Scale, HORIZONTAL +except ImportError: + from Tkinter import Tk, Toplevel, Frame, Label, Scale, HORIZONTAL + +from PIL import Image, ImageTk, ImageEnhance +import sys + +# +# enhancer widget + + +class Enhance(Frame): + def __init__(self, master, image, name, enhancer, lo, hi): + Frame.__init__(self, master) + + # set up the image + self.tkim = ImageTk.PhotoImage(image.mode, image.size) + self.enhancer = enhancer(image) + self.update("1.0") # normalize + + # image window + Label(self, image=self.tkim).pack() + + # scale + s = Scale(self, label=name, orient=HORIZONTAL, + from_=lo, to=hi, resolution=0.01, + command=self.update) + s.set(self.value) + s.pack() + + def update(self, value): + self.value = float(value) + self.tkim.paste(self.enhancer.enhance(self.value)) + +# +# main + +if len(sys.argv) != 2: + print("Usage: enhancer file") + sys.exit(1) + +root = Tk() + +im = Image.open(sys.argv[1]) + +im.thumbnail((200, 200)) + +Enhance(root, im, "Color", ImageEnhance.Color, 0.0, 4.0).pack() +Enhance(Toplevel(), im, "Sharpness", ImageEnhance.Sharpness, -2.0, 2.0).pack() +Enhance(Toplevel(), im, "Brightness", ImageEnhance.Brightness, -1.0, 3.0).pack() +Enhance(Toplevel(), im, "Contrast", ImageEnhance.Contrast, -1.0, 3.0).pack() + +root.mainloop() diff --git a/bin/explode.py b/bin/explode.py new file mode 100755 index 000000000..aec0a5cd5 --- /dev/null +++ b/bin/explode.py @@ -0,0 +1,112 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library +# $Id$ +# +# split an animation into a number of frame files +# + +from __future__ import print_function + +from PIL import Image +import os +import sys + + +class Interval(object): + + def __init__(self, interval="0"): + + self.setinterval(interval) + + def setinterval(self, interval): + + self.hilo = [] + + for s in interval.split(","): + if not s.strip(): + continue + try: + v = int(s) + if v < 0: + lo, hi = 0, -v + else: + lo = hi = v + except ValueError: + i = s.find("-") + lo, hi = int(s[:i]), int(s[i+1:]) + + self.hilo.append((hi, lo)) + + if not self.hilo: + self.hilo = [(sys.maxsize, 0)] + + def __getitem__(self, index): + + for hi, lo in self.hilo: + if hi >= index >= lo: + return 1 + return 0 + +# -------------------------------------------------------------------- +# main program + +html = 0 + +if sys.argv[1:2] == ["-h"]: + html = 1 + del sys.argv[1] + +if not sys.argv[2:]: + print() + print("Syntax: python explode.py infile template [range]") + print() + print("The template argument is used to construct the names of the") + print("individual frame files. The frames are numbered file001.ext,") + print("file002.ext, etc. You can insert %d to control the placement") + print("and syntax of the frame number.") + print() + print("The optional range argument specifies which frames to extract.") + print("You can give one or more ranges like 1-10, 5, -15 etc. If") + print("omitted, all frames are extracted.") + sys.exit(1) + +infile = sys.argv[1] +outfile = sys.argv[2] + +frames = Interval(",".join(sys.argv[3:])) + +try: + # check if outfile contains a placeholder + outfile % 1 +except TypeError: + file, ext = os.path.splitext(outfile) + outfile = file + "%03d" + ext + +ix = 1 + +im = Image.open(infile) + +if html: + file, ext = os.path.splitext(outfile) + html = open(file+".html", "w") + html.write("\n\n") + +while True: + + if frames[ix]: + im.save(outfile % ix) + print(outfile % ix) + + if html: + html.write("
\n" % outfile % ix) + + try: + im.seek(ix) + except EOFError: + break + + ix += 1 + +if html: + html.write("\n\n") diff --git a/bin/gifmaker.py b/bin/gifmaker.py new file mode 100755 index 000000000..74881e460 --- /dev/null +++ b/bin/gifmaker.py @@ -0,0 +1,31 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library +# $Id$ +# +# convert sequence format to GIF animation +# +# history: +# 97-01-03 fl created +# +# Copyright (c) Secret Labs AB 1997. All rights reserved. +# Copyright (c) Fredrik Lundh 1997. +# +# See the README file for information on usage and redistribution. +# + +from __future__ import print_function + +from PIL import Image + +if __name__ == "__main__": + + import sys + + if len(sys.argv) < 3: + print("GIFMAKER -- create GIF animations") + print("Usage: gifmaker infile outfile") + sys.exit(1) + + im = Image.open(sys.argv[1]) + im.save(sys.argv[2], save_all=True) diff --git a/bin/jp.py b/bin/jp.py new file mode 100755 index 000000000..82779072d --- /dev/null +++ b/bin/jp.py @@ -0,0 +1,54 @@ +#!/Users/admin/SHARE/bin/python3.5 + +import sys +import json +import argparse +from pprint import pformat + +import jmespath +from jmespath import exceptions + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('expression') + parser.add_argument('-f', '--filename', + help=('The filename containing the input data. ' + 'If a filename is not given then data is ' + 'read from stdin.')) + parser.add_argument('--ast', action='store_true', + help=('Pretty print the AST, do not search the data.')) + args = parser.parse_args() + expression = args.expression + if args.ast: + # Only print the AST + expression = jmespath.compile(args.expression) + sys.stdout.write(pformat(expression.parsed)) + sys.stdout.write('\n') + return 0 + if args.filename: + with open(args.filename, 'r') as f: + data = json.load(f) + else: + data = sys.stdin.read() + data = json.loads(data) + try: + sys.stdout.write(json.dumps( + jmespath.search(expression, data), indent=4)) + sys.stdout.write('\n') + except exceptions.ArityError as e: + sys.stderr.write("invalid-arity: %s\n" % e) + return 1 + except exceptions.JMESPathTypeError as e: + sys.stderr.write("invalid-type: %s\n" % e) + return 1 + except exceptions.UnknownFunctionError as e: + sys.stderr.write("unknown-function: %s\n" % e) + return 1 + except exceptions.ParseError as e: + sys.stderr.write("syntax-error: %s\n" % e) + return 1 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/bin/jsonschema b/bin/jsonschema new file mode 100755 index 000000000..525169637 --- /dev/null +++ b/bin/jsonschema @@ -0,0 +1,11 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from jsonschema.cli import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bin/markdown_py b/bin/markdown_py new file mode 100755 index 000000000..7763009b9 --- /dev/null +++ b/bin/markdown_py @@ -0,0 +1,34 @@ +#!/Users/admin/SHARE/bin/python3.5 +""" +Python Markdown, the Command Line Script +======================================== + +This is the command line script for Python Markdown. + +Basic use from the command line: + + markdown source.txt > destination.html + +Run "markdown --help" to see more options. + +See markdown/__init__.py for information on using Python Markdown as a module. + +## Authors and License + +Started by [Manfred Stienstra](http://www.dwerg.net/). Continued and +maintained by [Yuri Takhteyev](http://www.freewisdom.org), [Waylan +Limberg](http://achinghead.com/) and [Artem Yunusov](http://blog.splyer.com). + +Contact: markdown@freewisdom.org + +Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later) +Copyright 200? Django Software Foundation (OrderedDict implementation) +Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b) +Copyright 2004 Manfred Stienstra (the original version) + +License: BSD (see docs/LICENSE for details). +""" + +if __name__ == '__main__': + from markdown.__main__ import run + run() diff --git a/bin/newrelic-admin b/bin/newrelic-admin new file mode 100755 index 000000000..096c9045f --- /dev/null +++ b/bin/newrelic-admin @@ -0,0 +1,12 @@ +#!/Users/admin/SHARE/bin/python3.5 +# EASY-INSTALL-ENTRY-SCRIPT: 'newrelic==2.86.3.70','console_scripts','newrelic-admin' +__requires__ = 'newrelic==2.86.3.70' +import re +import sys +from pkg_resources import load_entry_point + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit( + load_entry_point('newrelic==2.86.3.70', 'console_scripts', 'newrelic-admin')() + ) diff --git a/bin/painter.py b/bin/painter.py new file mode 100755 index 000000000..f4d33f18c --- /dev/null +++ b/bin/painter.py @@ -0,0 +1,82 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library +# $Id$ +# +# this demo script illustrates pasting into an already displayed +# photoimage. note that the current version of Tk updates the whole +# image every time we paste, so to get decent performance, we split +# the image into a set of tiles. +# + +try: + from tkinter import Tk, Canvas, NW +except ImportError: + from Tkinter import Tk, Canvas, NW + +from PIL import Image, ImageTk +import sys + +# +# painter widget + + +class PaintCanvas(Canvas): + def __init__(self, master, image): + Canvas.__init__(self, master, width=image.size[0], height=image.size[1]) + + # fill the canvas + self.tile = {} + self.tilesize = tilesize = 32 + xsize, ysize = image.size + for x in range(0, xsize, tilesize): + for y in range(0, ysize, tilesize): + box = x, y, min(xsize, x+tilesize), min(ysize, y+tilesize) + tile = ImageTk.PhotoImage(image.crop(box)) + self.create_image(x, y, image=tile, anchor=NW) + self.tile[(x, y)] = box, tile + + self.image = image + + self.bind("", self.paint) + + def paint(self, event): + xy = event.x - 10, event.y - 10, event.x + 10, event.y + 10 + im = self.image.crop(xy) + + # process the image in some fashion + im = im.convert("L") + + self.image.paste(im, xy) + self.repair(xy) + + def repair(self, box): + # update canvas + dx = box[0] % self.tilesize + dy = box[1] % self.tilesize + for x in range(box[0]-dx, box[2]+1, self.tilesize): + for y in range(box[1]-dy, box[3]+1, self.tilesize): + try: + xy, tile = self.tile[(x, y)] + tile.paste(self.image.crop(xy)) + except KeyError: + pass # outside the image + self.update_idletasks() + +# +# main + +if len(sys.argv) != 2: + print("Usage: painter file") + sys.exit(1) + +root = Tk() + +im = Image.open(sys.argv[1]) + +if im.mode != "RGB": + im = im.convert("RGB") + +PaintCanvas(root, im).pack() + +root.mainloop() diff --git a/bin/pbr b/bin/pbr new file mode 100755 index 000000000..2a86bdeeb --- /dev/null +++ b/bin/pbr @@ -0,0 +1,11 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from pbr.cmd.main import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bin/pilconvert.py b/bin/pilconvert.py new file mode 100755 index 000000000..3135eb9c4 --- /dev/null +++ b/bin/pilconvert.py @@ -0,0 +1,99 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library. +# $Id$ +# +# convert image files +# +# History: +# 0.1 96-04-20 fl Created +# 0.2 96-10-04 fl Use draft mode when converting images +# 0.3 96-12-30 fl Optimize output (PNG, JPEG) +# 0.4 97-01-18 fl Made optimize an option (PNG, JPEG) +# 0.5 98-12-30 fl Fixed -f option (from Anthony Baxter) +# + +from __future__ import print_function + +import getopt +import string +import sys + +from PIL import Image + + +def usage(): + print("PIL Convert 0.5/1998-12-30 -- convert image files") + print("Usage: pilconvert [option] infile outfile") + print() + print("Options:") + print() + print(" -c convert to format (default is given by extension)") + print() + print(" -g convert to greyscale") + print(" -p convert to palette image (using standard palette)") + print(" -r convert to rgb") + print() + print(" -o optimize output (trade speed for size)") + print(" -q set compression quality (0-100, JPEG only)") + print() + print(" -f list supported file formats") + sys.exit(1) + +if len(sys.argv) == 1: + usage() + +try: + opt, argv = getopt.getopt(sys.argv[1:], "c:dfgopq:r") +except getopt.error as v: + print(v) + sys.exit(1) + +output_format = None +convert = None + +options = {} + +for o, a in opt: + + if o == "-f": + Image.init() + id = sorted(Image.ID) + print("Supported formats (* indicates output format):") + for i in id: + if i in Image.SAVE: + print(i+"*", end=' ') + else: + print(i, end=' ') + sys.exit(1) + + elif o == "-c": + output_format = a + + if o == "-g": + convert = "L" + elif o == "-p": + convert = "P" + elif o == "-r": + convert = "RGB" + + elif o == "-o": + options["optimize"] = 1 + elif o == "-q": + options["quality"] = string.atoi(a) + +if len(argv) != 2: + usage() + +try: + im = Image.open(argv[0]) + if convert and im.mode != convert: + im.draft(convert, im.size) + im = im.convert(convert) + if output_format: + im.save(argv[1], output_format, **options) + else: + im.save(argv[1], **options) +except: + print("cannot convert image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) diff --git a/bin/pildriver.py b/bin/pildriver.py new file mode 100755 index 000000000..0d0607364 --- /dev/null +++ b/bin/pildriver.py @@ -0,0 +1,526 @@ +#!/Users/admin/SHARE/bin/python3.5 +"""PILdriver, an image-processing calculator using PIL. + +An instance of class PILDriver is essentially a software stack machine +(Polish-notation interpreter) for sequencing PIL image +transformations. The state of the instance is the interpreter stack. + +The only method one will normally invoke after initialization is the +`execute' method. This takes an argument list of tokens, pushes them +onto the instance's stack, and then tries to clear the stack by +successive evaluation of PILdriver operators. Any part of the stack +not cleaned off persists and is part of the evaluation context for +the next call of the execute method. + +PILDriver doesn't catch any exceptions, on the theory that these +are actually diagnostic information that should be interpreted by +the calling code. + +When called as a script, the command-line arguments are passed to +a PILDriver instance. If there are no command-line arguments, the +module runs an interactive interpreter, each line of which is split into +space-separated tokens and passed to the execute method. + +In the method descriptions below, a first line beginning with the string +`usage:' means this method can be invoked with the token that follows +it. Following <>-enclosed arguments describe how the method interprets +the entries on the stack. Each argument specification begins with a +type specification: either `int', `float', `string', or `image'. + +All operations consume their arguments off the stack (use `dup' to +keep copies around). Use `verbose 1' to see the stack state displayed +before each operation. + +Usage examples: + + `show crop 0 0 200 300 open test.png' loads test.png, crops out a portion +of its upper-left-hand corner and displays the cropped portion. + + `save rotated.png rotate 30 open test.tiff' loads test.tiff, rotates it +30 degrees, and saves the result as rotated.png (in PNG format). +""" +# by Eric S. Raymond +# $Id$ + +# TO DO: +# 1. Add PILFont capabilities, once that's documented. +# 2. Add PILDraw operations. +# 3. Add support for composing and decomposing multiple-image files. +# + +from __future__ import print_function + +from PIL import Image + + +class PILDriver(object): + + verbose = 0 + + def do_verbose(self): + """usage: verbose + + Set verbosity flag from top of stack. + """ + self.verbose = int(self.do_pop()) + + # The evaluation stack (internal only) + + stack = [] # Stack of pending operations + + def push(self, item): + "Push an argument onto the evaluation stack." + self.stack.insert(0, item) + + def top(self): + "Return the top-of-stack element." + return self.stack[0] + + # Stack manipulation (callable) + + def do_clear(self): + """usage: clear + + Clear the stack. + """ + self.stack = [] + + def do_pop(self): + """usage: pop + + Discard the top element on the stack. + """ + return self.stack.pop(0) + + def do_dup(self): + """usage: dup + + Duplicate the top-of-stack item. + """ + if hasattr(self, 'format'): # If it's an image, do a real copy + dup = self.stack[0].copy() + else: + dup = self.stack[0] + self.push(dup) + + def do_swap(self): + """usage: swap + + Swap the top-of-stack item with the next one down. + """ + self.stack = [self.stack[1], self.stack[0]] + self.stack[2:] + + # Image module functions (callable) + + def do_new(self): + """usage: new : + + Create and push a greyscale image of given size and color. + """ + xsize = int(self.do_pop()) + ysize = int(self.do_pop()) + color = int(self.do_pop()) + self.push(Image.new("L", (xsize, ysize), color)) + + def do_open(self): + """usage: open + + Open the indicated image, read it, push the image on the stack. + """ + self.push(Image.open(self.do_pop())) + + def do_blend(self): + """usage: blend + + Replace two images and an alpha with the blended image. + """ + image1 = self.do_pop() + image2 = self.do_pop() + alpha = float(self.do_pop()) + self.push(Image.blend(image1, image2, alpha)) + + def do_composite(self): + """usage: composite + + Replace two images and a mask with their composite. + """ + image1 = self.do_pop() + image2 = self.do_pop() + mask = self.do_pop() + self.push(Image.composite(image1, image2, mask)) + + def do_merge(self): + """usage: merge + [ [ []]] + + Merge top-of stack images in a way described by the mode. + """ + mode = self.do_pop() + bandlist = [] + for band in mode: + bandlist.append(self.do_pop()) + self.push(Image.merge(mode, bandlist)) + + # Image class methods + + def do_convert(self): + """usage: convert + + Convert the top image to the given mode. + """ + mode = self.do_pop() + image = self.do_pop() + self.push(image.convert(mode)) + + def do_copy(self): + """usage: copy + + Make and push a true copy of the top image. + """ + self.dup() + + def do_crop(self): + """usage: crop + + + Crop and push a rectangular region from the current image. + """ + left = int(self.do_pop()) + upper = int(self.do_pop()) + right = int(self.do_pop()) + lower = int(self.do_pop()) + image = self.do_pop() + self.push(image.crop((left, upper, right, lower))) + + def do_draft(self): + """usage: draft + + Configure the loader for a given mode and size. + """ + mode = self.do_pop() + xsize = int(self.do_pop()) + ysize = int(self.do_pop()) + self.push(self.draft(mode, (xsize, ysize))) + + def do_filter(self): + """usage: filter + + Process the top image with the given filter. + """ + from PIL import ImageFilter + imageFilter = getattr(ImageFilter, self.do_pop().upper()) + image = self.do_pop() + self.push(image.filter(imageFilter)) + + def do_getbbox(self): + """usage: getbbox + + Push left, upper, right, and lower pixel coordinates of the top image. + """ + bounding_box = self.do_pop().getbbox() + self.push(bounding_box[3]) + self.push(bounding_box[2]) + self.push(bounding_box[1]) + self.push(bounding_box[0]) + + def do_getextrema(self): + """usage: extrema + + Push minimum and maximum pixel values of the top image. + """ + extrema = self.do_pop().extrema() + self.push(extrema[1]) + self.push(extrema[0]) + + def do_offset(self): + """usage: offset + + Offset the pixels in the top image. + """ + xoff = int(self.do_pop()) + yoff = int(self.do_pop()) + image = self.do_pop() + self.push(image.offset(xoff, yoff)) + + def do_paste(self): + """usage: paste + + + Paste figure image into ground with upper left at given offsets. + """ + figure = self.do_pop() + xoff = int(self.do_pop()) + yoff = int(self.do_pop()) + ground = self.do_pop() + if figure.mode == "RGBA": + ground.paste(figure, (xoff, yoff), figure) + else: + ground.paste(figure, (xoff, yoff)) + self.push(ground) + + def do_resize(self): + """usage: resize + + Resize the top image. + """ + ysize = int(self.do_pop()) + xsize = int(self.do_pop()) + image = self.do_pop() + self.push(image.resize((xsize, ysize))) + + def do_rotate(self): + """usage: rotate + + Rotate image through a given angle + """ + angle = int(self.do_pop()) + image = self.do_pop() + self.push(image.rotate(angle)) + + def do_save(self): + """usage: save + + Save image with default options. + """ + filename = self.do_pop() + image = self.do_pop() + image.save(filename) + + def do_save2(self): + """usage: save2 + + Save image with specified options. + """ + filename = self.do_pop() + options = self.do_pop() + image = self.do_pop() + image.save(filename, None, options) + + def do_show(self): + """usage: show + + Display and pop the top image. + """ + self.do_pop().show() + + def do_thumbnail(self): + """usage: thumbnail + + Modify the top image in the stack to contain a thumbnail of itself. + """ + ysize = int(self.do_pop()) + xsize = int(self.do_pop()) + self.top().thumbnail((xsize, ysize)) + + def do_transpose(self): + """usage: transpose + + Transpose the top image. + """ + transpose = self.do_pop().upper() + image = self.do_pop() + self.push(image.transpose(transpose)) + + # Image attributes + + def do_format(self): + """usage: format + + Push the format of the top image onto the stack. + """ + self.push(self.do_pop().format) + + def do_mode(self): + """usage: mode + + Push the mode of the top image onto the stack. + """ + self.push(self.do_pop().mode) + + def do_size(self): + """usage: size + + Push the image size on the stack as (y, x). + """ + size = self.do_pop().size + self.push(size[0]) + self.push(size[1]) + + # ImageChops operations + + def do_invert(self): + """usage: invert + + Invert the top image. + """ + from PIL import ImageChops + self.push(ImageChops.invert(self.do_pop())) + + def do_lighter(self): + """usage: lighter + + Pop the two top images, push an image of the lighter pixels of both. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.lighter(image1, image2)) + + def do_darker(self): + """usage: darker + + Pop the two top images, push an image of the darker pixels of both. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.darker(image1, image2)) + + def do_difference(self): + """usage: difference + + Pop the two top images, push the difference image + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.difference(image1, image2)) + + def do_multiply(self): + """usage: multiply + + Pop the two top images, push the multiplication image. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + self.push(ImageChops.multiply(image1, image2)) + + def do_screen(self): + """usage: screen + + Pop the two top images, superimpose their inverted versions. + """ + from PIL import ImageChops + image2 = self.do_pop() + image1 = self.do_pop() + self.push(ImageChops.screen(image1, image2)) + + def do_add(self): + """usage: add + + Pop the two top images, produce the scaled sum with offset. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + scale = float(self.do_pop()) + offset = int(self.do_pop()) + self.push(ImageChops.add(image1, image2, scale, offset)) + + def do_subtract(self): + """usage: subtract + + Pop the two top images, produce the scaled difference with offset. + """ + from PIL import ImageChops + image1 = self.do_pop() + image2 = self.do_pop() + scale = float(self.do_pop()) + offset = int(self.do_pop()) + self.push(ImageChops.subtract(image1, image2, scale, offset)) + + # ImageEnhance classes + + def do_color(self): + """usage: color + + Enhance color in the top image. + """ + from PIL import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Color(image) + self.push(enhancer.enhance(factor)) + + def do_contrast(self): + """usage: contrast + + Enhance contrast in the top image. + """ + from PIL import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Contrast(image) + self.push(enhancer.enhance(factor)) + + def do_brightness(self): + """usage: brightness + + Enhance brightness in the top image. + """ + from PIL import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Brightness(image) + self.push(enhancer.enhance(factor)) + + def do_sharpness(self): + """usage: sharpness + + Enhance sharpness in the top image. + """ + from PIL import ImageEnhance + factor = float(self.do_pop()) + image = self.do_pop() + enhancer = ImageEnhance.Sharpness(image) + self.push(enhancer.enhance(factor)) + + # The interpreter loop + + def execute(self, list): + "Interpret a list of PILDriver commands." + list.reverse() + while len(list) > 0: + self.push(list[0]) + list = list[1:] + if self.verbose: + print("Stack: " + repr(self.stack)) + top = self.top() + if not isinstance(top, str): + continue + funcname = "do_" + top + if not hasattr(self, funcname): + continue + else: + self.do_pop() + func = getattr(self, funcname) + func() + +if __name__ == '__main__': + import sys + + # If we see command-line arguments, interpret them as a stack state + # and execute. Otherwise go interactive. + + driver = PILDriver() + if len(sys.argv[1:]) > 0: + driver.execute(sys.argv[1:]) + else: + print("PILDriver says hello.") + while True: + try: + if sys.version_info[0] >= 3: + line = input('pildriver> ') + else: + line = raw_input('pildriver> ') + except EOFError: + print("\nPILDriver says goodbye.") + break + driver.execute(line.split()) + print(driver.stack) + +# The following sets edit modes for GNU EMACS +# Local Variables: +# mode:python +# End: diff --git a/bin/pilfile.py b/bin/pilfile.py new file mode 100755 index 000000000..ba43609f1 --- /dev/null +++ b/bin/pilfile.py @@ -0,0 +1,101 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library. +# $Id$ +# +# a utility to identify image files +# +# this script identifies image files, extracting size and +# pixel mode information for known file formats. Note that +# you don't need the PIL C extension to use this module. +# +# History: +# 0.0 1995-09-01 fl Created +# 0.1 1996-05-18 fl Modified options, added debugging mode +# 0.2 1996-12-29 fl Added verify mode +# 0.3 1999-06-05 fl Don't mess up on class exceptions (1.5.2 and later) +# 0.4 2003-09-30 fl Expand wildcards on Windows; robustness tweaks +# + +from __future__ import print_function + +import getopt +import glob +import logging +import sys + +from PIL import Image + +if len(sys.argv) == 1: + print("PIL File 0.4/2003-09-30 -- identify image files") + print("Usage: pilfile [option] files...") + print("Options:") + print(" -f list supported file formats") + print(" -i show associated info and tile data") + print(" -v verify file headers") + print(" -q quiet, don't warn for unidentified/missing/broken files") + sys.exit(1) + +try: + opt, args = getopt.getopt(sys.argv[1:], "fqivD") +except getopt.error as v: + print(v) + sys.exit(1) + +verbose = quiet = verify = 0 +logging_level = "WARNING" + +for o, a in opt: + if o == "-f": + Image.init() + id = sorted(Image.ID) + print("Supported formats:") + for i in id: + print(i, end=' ') + sys.exit(1) + elif o == "-i": + verbose = 1 + elif o == "-q": + quiet = 1 + elif o == "-v": + verify = 1 + elif o == "-D": + logging_level = "DEBUG" + +logging.basicConfig(level=logging_level) + + +def globfix(files): + # expand wildcards where necessary + if sys.platform == "win32": + out = [] + for file in files: + if glob.has_magic(file): + out.extend(glob.glob(file)) + else: + out.append(file) + return out + return files + +for file in globfix(args): + try: + im = Image.open(file) + print("%s:" % file, im.format, "%dx%d" % im.size, im.mode, end=' ') + if verbose: + print(im.info, im.tile, end=' ') + print() + if verify: + try: + im.verify() + except: + if not quiet: + print("failed to verify image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) + except IOError as v: + if not quiet: + print(file, "failed:", v) + except: + import traceback + if not quiet: + print(file, "failed:", "unexpected error") + traceback.print_exc(file=sys.stdout) diff --git a/bin/pilfont.py b/bin/pilfont.py new file mode 100755 index 000000000..d112e2524 --- /dev/null +++ b/bin/pilfont.py @@ -0,0 +1,57 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library +# $Id$ +# +# PIL raster font compiler +# +# history: +# 1997-08-25 fl created +# 2002-03-10 fl use "from PIL import" +# + +from __future__ import print_function + +import glob +import sys + +# drivers +from PIL import BdfFontFile +from PIL import PcfFontFile + +VERSION = "0.4" + +if len(sys.argv) <= 1: + print("PILFONT", VERSION, "-- PIL font compiler.") + print() + print("Usage: pilfont fontfiles...") + print() + print("Convert given font files to the PIL raster font format.") + print("This version of pilfont supports X BDF and PCF fonts.") + sys.exit(1) + +files = [] +for f in sys.argv[1:]: + files = files + glob.glob(f) + +for f in files: + + print(f + "...", end=' ') + + try: + + fp = open(f, "rb") + + try: + p = PcfFontFile.PcfFontFile(fp) + except SyntaxError: + fp.seek(0) + p = BdfFontFile.BdfFontFile(fp) + + p.save(f) + + except (SyntaxError, IOError): + print("failed") + + else: + print("OK") diff --git a/bin/pilprint.py b/bin/pilprint.py new file mode 100755 index 000000000..0a1b741d4 --- /dev/null +++ b/bin/pilprint.py @@ -0,0 +1,102 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library. +# $Id$ +# +# print image files to postscript printer +# +# History: +# 0.1 1996-04-20 fl Created +# 0.2 1996-10-04 fl Use draft mode when converting. +# 0.3 2003-05-06 fl Fixed a typo or two. +# + +from __future__ import print_function +import getopt +import os +import sys +import subprocess + +VERSION = "pilprint 0.3/2003-05-05" + +from PIL import Image +from PIL import PSDraw + +letter = (1.0*72, 1.0*72, 7.5*72, 10.0*72) + + +def description(filepath, image): + title = os.path.splitext(os.path.split(filepath)[1])[0] + format = " (%dx%d " + if image.format: + format = " (" + image.format + " %dx%d " + return title + format % image.size + image.mode + ")" + +if len(sys.argv) == 1: + print("PIL Print 0.3/2003-05-05 -- print image files") + print("Usage: pilprint files...") + print("Options:") + print(" -c colour printer (default is monochrome)") + print(" -d debug (show available drivers)") + print(" -p print via lpr (default is stdout)") + print(" -P same as -p but use given printer") + sys.exit(1) + +try: + opt, argv = getopt.getopt(sys.argv[1:], "cdpP:") +except getopt.error as v: + print(v) + sys.exit(1) + +printerArgs = [] # print to stdout +monochrome = 1 # reduce file size for most common case + +for o, a in opt: + if o == "-d": + # debug: show available drivers + Image.init() + print(Image.ID) + sys.exit(1) + elif o == "-c": + # colour printer + monochrome = 0 + elif o == "-p": + # default printer channel + printerArgs = ["lpr"] + elif o == "-P": + # printer channel + printerArgs = ["lpr", "-P%s" % a] + +for filepath in argv: + try: + + im = Image.open(filepath) + + title = description(filepath, im) + + if monochrome and im.mode not in ["1", "L"]: + im.draft("L", im.size) + im = im.convert("L") + + if printerArgs: + p = subprocess.Popen(printerArgs, stdin=subprocess.PIPE) + fp = p.stdin + else: + fp = sys.stdout + + ps = PSDraw.PSDraw(fp) + + ps.begin_document() + ps.setfont("Helvetica-Narrow-Bold", 18) + ps.text((letter[0], letter[3]+24), title) + ps.setfont("Helvetica-Narrow-Bold", 8) + ps.text((letter[0], letter[1]-30), VERSION) + ps.image(letter, im) + ps.end_document() + + if printerArgs: + fp.close() + + except: + print("cannot print image", end=' ') + print("(%s:%s)" % (sys.exc_info()[0], sys.exc_info()[1])) diff --git a/bin/pip b/bin/pip new file mode 100755 index 000000000..3d215bf98 --- /dev/null +++ b/bin/pip @@ -0,0 +1,11 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from pip import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bin/pip3 b/bin/pip3 new file mode 100755 index 000000000..3d215bf98 --- /dev/null +++ b/bin/pip3 @@ -0,0 +1,11 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from pip import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bin/pip3.5 b/bin/pip3.5 new file mode 100755 index 000000000..3d215bf98 --- /dev/null +++ b/bin/pip3.5 @@ -0,0 +1,11 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from pip import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bin/player.py b/bin/player.py new file mode 100755 index 000000000..ba4720d48 --- /dev/null +++ b/bin/player.py @@ -0,0 +1,102 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library +# $Id$ +# + +from __future__ import print_function + +try: + from tkinter import * +except ImportError: + from Tkinter import * + +from PIL import Image, ImageTk +import sys + + +# -------------------------------------------------------------------- +# an image animation player + +class UI(Label): + + def __init__(self, master, im): + if isinstance(im, list): + # list of images + self.im = im[1:] + im = self.im[0] + else: + # sequence + self.im = im + + if im.mode == "1": + self.image = ImageTk.BitmapImage(im, foreground="white") + else: + self.image = ImageTk.PhotoImage(im) + + Label.__init__(self, master, image=self.image, bg="black", bd=0) + + self.update() + + try: + duration = im.info["duration"] + except KeyError: + duration = 100 + self.after(duration, self.next) + + def next(self): + + if isinstance(self.im, list): + + try: + im = self.im[0] + del self.im[0] + self.image.paste(im) + except IndexError: + return # end of list + + else: + + try: + im = self.im + im.seek(im.tell() + 1) + self.image.paste(im) + except EOFError: + return # end of file + + try: + duration = im.info["duration"] + except KeyError: + duration = 100 + self.after(duration, self.next) + + self.update_idletasks() + + +# -------------------------------------------------------------------- +# script interface + +if __name__ == "__main__": + + if not sys.argv[1:]: + print("Syntax: python player.py imagefile(s)") + sys.exit(1) + + filename = sys.argv[1] + + root = Tk() + root.title(filename) + + if len(sys.argv) > 2: + # list of images + print("loading...") + im = [] + for filename in sys.argv[1:]: + im.append(Image.open(filename)) + else: + # sequence + im = Image.open(filename) + + UI(root, im).pack() + + root.mainloop() diff --git a/bin/python b/bin/python new file mode 120000 index 000000000..f549cead8 --- /dev/null +++ b/bin/python @@ -0,0 +1 @@ +python3.5 \ No newline at end of file diff --git a/bin/python3 b/bin/python3 new file mode 120000 index 000000000..f549cead8 --- /dev/null +++ b/bin/python3 @@ -0,0 +1 @@ +python3.5 \ No newline at end of file diff --git a/bin/python3.5 b/bin/python3.5 new file mode 120000 index 000000000..7782f2c00 --- /dev/null +++ b/bin/python3.5 @@ -0,0 +1 @@ +/usr/local/bin/python3.5 \ No newline at end of file diff --git a/bin/raven b/bin/raven new file mode 100755 index 000000000..4a5fc51ec --- /dev/null +++ b/bin/raven @@ -0,0 +1,11 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# -*- coding: utf-8 -*- +import re +import sys + +from raven.scripts.runner import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/bin/rst2html.py b/bin/rst2html.py new file mode 100755 index 000000000..dec073c99 --- /dev/null +++ b/bin/rst2html.py @@ -0,0 +1,23 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# $Id: rst2html.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing HTML. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates (X)HTML documents from standalone reStructuredText ' + 'sources. ' + default_description) + +publish_cmdline(writer_name='html', description=description) diff --git a/bin/rst2html5.py b/bin/rst2html5.py new file mode 100755 index 000000000..07a9615a1 --- /dev/null +++ b/bin/rst2html5.py @@ -0,0 +1,35 @@ +#!/Users/admin/SHARE/bin/python3.5 +# -*- coding: utf8 -*- +# :Copyright: © 2015 Günter Milde. +# :License: Released under the terms of the `2-Clause BSD license`_, in short: +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. +# This file is offered as-is, without any warranty. +# +# .. _2-Clause BSD license: http://www.spdx.org/licenses/BSD-2-Clause +# +# Revision: $Revision: 7847 $ +# Date: $Date: 2015-03-17 18:30:47 +0100 (Di, 17. Mär 2015) $ + +""" +A minimal front end to the Docutils Publisher, producing HTML 5 documents. + +The output also conforms to XHTML 1.0 transitional +(except for the doctype declaration). +""" + +try: + import locale # module missing in Jython + locale.setlocale(locale.LC_ALL, '') +except locale.Error: + pass + +from docutils.core import publish_cmdline, default_description + +description = (u'Generates HTML 5 documents from standalone ' + u'reStructuredText sources ' + + default_description) + +publish_cmdline(writer_name='html5', description=description) diff --git a/bin/rst2latex.py b/bin/rst2latex.py new file mode 100755 index 000000000..2717c1f97 --- /dev/null +++ b/bin/rst2latex.py @@ -0,0 +1,26 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# $Id: rst2latex.py 5905 2009-04-16 12:04:49Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing LaTeX. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline + +description = ('Generates LaTeX documents from standalone reStructuredText ' + 'sources. ' + 'Reads from (default is stdin) and writes to ' + ' (default is stdout). See ' + ' for ' + 'the full reference.') + +publish_cmdline(writer_name='latex', description=description) diff --git a/bin/rst2man.py b/bin/rst2man.py new file mode 100755 index 000000000..84eebc9eb --- /dev/null +++ b/bin/rst2man.py @@ -0,0 +1,26 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# Author: +# Contact: grubert@users.sf.net +# Copyright: This module has been placed in the public domain. + +""" +man.py +====== + +This module provides a simple command line interface that uses the +man page writer to output from ReStructuredText source. +""" + +import locale +try: + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description +from docutils.writers import manpage + +description = ("Generates plain unix manual documents. " + default_description) + +publish_cmdline(writer=manpage.Writer(), description=description) diff --git a/bin/rst2odt.py b/bin/rst2odt.py new file mode 100755 index 000000000..b15e6dc2f --- /dev/null +++ b/bin/rst2odt.py @@ -0,0 +1,30 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# $Id: rst2odt.py 5839 2009-01-07 19:09:28Z dkuhlman $ +# Author: Dave Kuhlman +# Copyright: This module has been placed in the public domain. + +""" +A front end to the Docutils Publisher, producing OpenOffice documents. +""" + +import sys +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline_to_binary, default_description +from docutils.writers.odf_odt import Writer, Reader + + +description = ('Generates OpenDocument/OpenOffice/ODF documents from ' + 'standalone reStructuredText sources. ' + default_description) + + +writer = Writer() +reader = Reader() +output = publish_cmdline_to_binary(reader=reader, writer=writer, + description=description) + diff --git a/bin/rst2odt_prepstyles.py b/bin/rst2odt_prepstyles.py new file mode 100755 index 000000000..36525a758 --- /dev/null +++ b/bin/rst2odt_prepstyles.py @@ -0,0 +1,67 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# $Id: rst2odt_prepstyles.py 5839 2009-01-07 19:09:28Z dkuhlman $ +# Author: Dave Kuhlman +# Copyright: This module has been placed in the public domain. + +""" +Fix a word-processor-generated styles.odt for odtwriter use: Drop page size +specifications from styles.xml in STYLE_FILE.odt. +""" + +# +# Author: Michael Schutte + +from lxml import etree +import sys +import zipfile +from tempfile import mkstemp +import shutil +import os + +NAMESPACES = { + "style": "urn:oasis:names:tc:opendocument:xmlns:style:1.0", + "fo": "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" +} + +def prepstyle(filename): + + zin = zipfile.ZipFile(filename) + styles = zin.read("styles.xml") + + root = etree.fromstring(styles) + for el in root.xpath("//style:page-layout-properties", + namespaces=NAMESPACES): + for attr in el.attrib: + if attr.startswith("{%s}" % NAMESPACES["fo"]): + del el.attrib[attr] + + tempname = mkstemp() + zout = zipfile.ZipFile(os.fdopen(tempname[0], "w"), "w", + zipfile.ZIP_DEFLATED) + + for item in zin.infolist(): + if item.filename == "styles.xml": + zout.writestr(item, etree.tostring(root)) + else: + zout.writestr(item, zin.read(item.filename)) + + zout.close() + zin.close() + shutil.move(tempname[1], filename) + + +def main(): + args = sys.argv[1:] + if len(args) != 1: + print >> sys.stderr, __doc__ + print >> sys.stderr, "Usage: %s STYLE_FILE.odt\n" % sys.argv[0] + sys.exit(1) + filename = args[0] + prepstyle(filename) + +if __name__ == '__main__': + main() + + +# vim:tw=78:sw=4:sts=4:et: diff --git a/bin/rst2pseudoxml.py b/bin/rst2pseudoxml.py new file mode 100755 index 000000000..b190d1e5b --- /dev/null +++ b/bin/rst2pseudoxml.py @@ -0,0 +1,23 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# $Id: rst2pseudoxml.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing pseudo-XML. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates pseudo-XML from standalone reStructuredText ' + 'sources (for testing purposes). ' + default_description) + +publish_cmdline(description=description) diff --git a/bin/rst2s5.py b/bin/rst2s5.py new file mode 100755 index 000000000..3f3046729 --- /dev/null +++ b/bin/rst2s5.py @@ -0,0 +1,24 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# $Id: rst2s5.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: Chris Liechti +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing HTML slides using +the S5 template system. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates S5 (X)HTML slideshow documents from standalone ' + 'reStructuredText sources. ' + default_description) + +publish_cmdline(writer_name='s5', description=description) diff --git a/bin/rst2xetex.py b/bin/rst2xetex.py new file mode 100755 index 000000000..6b65a760f --- /dev/null +++ b/bin/rst2xetex.py @@ -0,0 +1,27 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# $Id: rst2xetex.py 7847 2015-03-17 17:30:47Z milde $ +# Author: Guenter Milde +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing Lua/XeLaTeX code. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline + +description = ('Generates LaTeX documents from standalone reStructuredText ' + 'sources for compilation with the Unicode-aware TeX variants ' + 'XeLaTeX or LuaLaTeX. ' + 'Reads from (default is stdin) and writes to ' + ' (default is stdout). See ' + ' for ' + 'the full reference.') + +publish_cmdline(writer_name='xetex', description=description) diff --git a/bin/rst2xml.py b/bin/rst2xml.py new file mode 100755 index 000000000..617ab2746 --- /dev/null +++ b/bin/rst2xml.py @@ -0,0 +1,23 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# $Id: rst2xml.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing Docutils XML. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates Docutils-native XML from standalone ' + 'reStructuredText sources. ' + default_description) + +publish_cmdline(writer_name='xml', description=description) diff --git a/bin/rstpep2html.py b/bin/rstpep2html.py new file mode 100755 index 000000000..e1e7523e1 --- /dev/null +++ b/bin/rstpep2html.py @@ -0,0 +1,25 @@ +#!/Users/admin/SHARE/bin/python3.5 + +# $Id: rstpep2html.py 4564 2006-05-21 20:44:42Z wiemann $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing HTML from PEP +(Python Enhancement Proposal) documents. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates (X)HTML from reStructuredText-format PEP files. ' + + default_description) + +publish_cmdline(reader_name='pep', writer_name='pep_html', + description=description) diff --git a/bin/sharectl b/bin/sharectl new file mode 100755 index 000000000..fbe549688 --- /dev/null +++ b/bin/sharectl @@ -0,0 +1,12 @@ +#!/Users/admin/SHARE/bin/python +# EASY-INSTALL-ENTRY-SCRIPT: 'share','console_scripts','sharectl' +__requires__ = 'share' +import re +import sys +from pkg_resources import load_entry_point + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit( + load_entry_point('share', 'console_scripts', 'sharectl')() + ) diff --git a/bin/thresholder.py b/bin/thresholder.py new file mode 100755 index 000000000..bc6d8865e --- /dev/null +++ b/bin/thresholder.py @@ -0,0 +1,78 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library +# $Id$ +# +# this demo script illustrates how a 1-bit BitmapImage can be used +# as a dynamically updated overlay +# + +try: + from tkinter import * +except ImportError: + from Tkinter import * + +from PIL import Image, ImageTk +import sys + +# +# an image viewer + + +class UI(Frame): + def __init__(self, master, im, value=128): + Frame.__init__(self, master) + + self.image = im + self.value = value + + self.canvas = Canvas(self, width=im.size[0], height=im.size[1]) + self.backdrop = ImageTk.PhotoImage(im) + self.canvas.create_image(0, 0, image=self.backdrop, anchor=NW) + self.canvas.pack() + + scale = Scale(self, orient=HORIZONTAL, from_=0, to=255, + resolution=1, command=self.update_scale, length=256) + scale.set(value) + scale.bind("", self.redraw) + scale.pack() + + # uncomment the following line for instant feedback (might + # be too slow on some platforms) + # self.redraw() + + def update_scale(self, value): + self.value = float(value) + + self.redraw() + + def redraw(self, event=None): + + # create overlay (note the explicit conversion to mode "1") + im = self.image.point(lambda v, t=self.value: v >= t, "1") + self.overlay = ImageTk.BitmapImage(im, foreground="green") + + # update canvas + self.canvas.delete("overlay") + self.canvas.create_image(0, 0, image=self.overlay, anchor=NW, + tags="overlay") + +# -------------------------------------------------------------------- +# main + +if len(sys.argv) != 2: + print("Usage: thresholder file") + sys.exit(1) + +root = Tk() + +im = Image.open(sys.argv[1]) + +if im.mode != "L": + im = im.convert("L") + +# im.thumbnail((320,200)) + +UI(root, im).pack() + +root.mainloop() diff --git a/bin/viewer.py b/bin/viewer.py new file mode 100755 index 000000000..217026e9a --- /dev/null +++ b/bin/viewer.py @@ -0,0 +1,54 @@ +#!/Users/admin/SHARE/bin/python3.5 +# +# The Python Imaging Library +# $Id$ +# + +from __future__ import print_function + +try: + from tkinter import Tk, Label +except ImportError: + from Tkinter import Tk, Label + +from PIL import Image, ImageTk + +# +# an image viewer + + +class UI(Label): + + def __init__(self, master, im): + + if im.mode == "1": + # bitmap image + self.image = ImageTk.BitmapImage(im, foreground="white") + Label.__init__(self, master, image=self.image, bg="black", bd=0) + + else: + # photo image + self.image = ImageTk.PhotoImage(im) + Label.__init__(self, master, image=self.image, bd=0) + +# +# script interface + +if __name__ == "__main__": + + import sys + + if not sys.argv[1:]: + print("Syntax: python viewer.py imagefile") + sys.exit(1) + + filename = sys.argv[1] + + root = Tk() + root.title(filename) + + im = Image.open(filename) + + UI(root, im).pack() + + root.mainloop() diff --git a/include/site/python3.5/greenlet/greenlet.h b/include/site/python3.5/greenlet/greenlet.h new file mode 100644 index 000000000..0eceecbf1 --- /dev/null +++ b/include/site/python3.5/greenlet/greenlet.h @@ -0,0 +1,148 @@ +/* vim:set noet ts=8 sw=8 : */ + +/* Greenlet object interface */ + +#ifndef Py_GREENLETOBJECT_H +#define Py_GREENLETOBJECT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define GREENLET_VERSION "0.4.12" + +typedef struct _greenlet { + PyObject_HEAD + char* stack_start; + char* stack_stop; + char* stack_copy; + intptr_t stack_saved; + struct _greenlet* stack_prev; + struct _greenlet* parent; + PyObject* run_info; + struct _frame* top_frame; + int recursion_depth; + PyObject* weakreflist; + PyObject* exc_type; + PyObject* exc_value; + PyObject* exc_traceback; + PyObject* dict; +} PyGreenlet; + +#define PyGreenlet_Check(op) PyObject_TypeCheck(op, &PyGreenlet_Type) +#define PyGreenlet_MAIN(op) (((PyGreenlet*)(op))->stack_stop == (char*) -1) +#define PyGreenlet_STARTED(op) (((PyGreenlet*)(op))->stack_stop != NULL) +#define PyGreenlet_ACTIVE(op) (((PyGreenlet*)(op))->stack_start != NULL) +#define PyGreenlet_GET_PARENT(op) (((PyGreenlet*)(op))->parent) + +#if (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 7) || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 1) || PY_MAJOR_VERSION > 3 +#define GREENLET_USE_PYCAPSULE +#endif + +/* C API functions */ + +/* Total number of symbols that are exported */ +#define PyGreenlet_API_pointers 8 + +#define PyGreenlet_Type_NUM 0 +#define PyExc_GreenletError_NUM 1 +#define PyExc_GreenletExit_NUM 2 + +#define PyGreenlet_New_NUM 3 +#define PyGreenlet_GetCurrent_NUM 4 +#define PyGreenlet_Throw_NUM 5 +#define PyGreenlet_Switch_NUM 6 +#define PyGreenlet_SetParent_NUM 7 + +#ifndef GREENLET_MODULE +/* This section is used by modules that uses the greenlet C API */ +static void **_PyGreenlet_API = NULL; + +#define PyGreenlet_Type (*(PyTypeObject *) _PyGreenlet_API[PyGreenlet_Type_NUM]) + +#define PyExc_GreenletError \ + ((PyObject *) _PyGreenlet_API[PyExc_GreenletError_NUM]) + +#define PyExc_GreenletExit \ + ((PyObject *) _PyGreenlet_API[PyExc_GreenletExit_NUM]) + +/* + * PyGreenlet_New(PyObject *args) + * + * greenlet.greenlet(run, parent=None) + */ +#define PyGreenlet_New \ + (* (PyGreenlet * (*)(PyObject *run, PyGreenlet *parent)) \ + _PyGreenlet_API[PyGreenlet_New_NUM]) + +/* + * PyGreenlet_GetCurrent(void) + * + * greenlet.getcurrent() + */ +#define PyGreenlet_GetCurrent \ + (* (PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM]) + +/* + * PyGreenlet_Throw( + * PyGreenlet *greenlet, + * PyObject *typ, + * PyObject *val, + * PyObject *tb) + * + * g.throw(...) + */ +#define PyGreenlet_Throw \ + (* (PyObject * (*) \ + (PyGreenlet *self, PyObject *typ, PyObject *val, PyObject *tb)) \ + _PyGreenlet_API[PyGreenlet_Throw_NUM]) + +/* + * PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args) + * + * g.switch(*args, **kwargs) + */ +#define PyGreenlet_Switch \ + (* (PyObject * (*)(PyGreenlet *greenlet, PyObject *args, PyObject *kwargs)) \ + _PyGreenlet_API[PyGreenlet_Switch_NUM]) + +/* + * PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent) + * + * g.parent = new_parent + */ +#define PyGreenlet_SetParent \ + (* (int (*)(PyGreenlet *greenlet, PyGreenlet *nparent)) \ + _PyGreenlet_API[PyGreenlet_SetParent_NUM]) + +/* Macro that imports greenlet and initializes C API */ +#ifdef GREENLET_USE_PYCAPSULE +#define PyGreenlet_Import() \ +{ \ + _PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \ +} +#else +#define PyGreenlet_Import() \ +{ \ + PyObject *module = PyImport_ImportModule("greenlet"); \ + if (module != NULL) { \ + PyObject *c_api_object = PyObject_GetAttrString( \ + module, "_C_API"); \ + if (c_api_object != NULL && PyCObject_Check(c_api_object)) { \ + _PyGreenlet_API = \ + (void **) PyCObject_AsVoidPtr(c_api_object); \ + Py_DECREF(c_api_object); \ + } \ + Py_DECREF(module); \ + } \ +} +#endif + +#endif /* GREENLET_MODULE */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_GREENLETOBJECT_H */ diff --git a/pip-selfcheck.json b/pip-selfcheck.json new file mode 100644 index 000000000..5bfa76afd --- /dev/null +++ b/pip-selfcheck.json @@ -0,0 +1 @@ +{"last_check":"2017-07-05T15:33:58Z","pypi_version":"9.0.1"} \ No newline at end of file diff --git a/pyvenv.cfg b/pyvenv.cfg new file mode 100644 index 000000000..aba37e81a --- /dev/null +++ b/pyvenv.cfg @@ -0,0 +1,3 @@ +home = /usr/local/bin +include-system-site-packages = false +version = 3.5.3 diff --git a/share/doc/networkx-1.11/INSTALL.txt b/share/doc/networkx-1.11/INSTALL.txt new file mode 100644 index 000000000..0df6ae25f --- /dev/null +++ b/share/doc/networkx-1.11/INSTALL.txt @@ -0,0 +1,3 @@ +See doc/source/install.rst +or +http://networkx.github.io/documentation/latest/install.html diff --git a/share/doc/networkx-1.11/LICENSE.txt b/share/doc/networkx-1.11/LICENSE.txt new file mode 100644 index 000000000..5d07e773c --- /dev/null +++ b/share/doc/networkx-1.11/LICENSE.txt @@ -0,0 +1,40 @@ +License +======= +NetworkX is distributed with the BSD license. + +:: + + Copyright (C) 2004-2016, NetworkX Developers + Aric Hagberg + Dan Schult + Pieter Swart + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of the NetworkX Developers nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/share/doc/networkx-1.11/examples/3d_drawing/mayavi2_spring.py b/share/doc/networkx-1.11/examples/3d_drawing/mayavi2_spring.py new file mode 100644 index 000000000..b9eda22b7 --- /dev/null +++ b/share/doc/networkx-1.11/examples/3d_drawing/mayavi2_spring.py @@ -0,0 +1,37 @@ +# needs mayavi2 +# run with ipython -wthread +import networkx as nx +import numpy as np +from enthought.mayavi import mlab + +# some graphs to try +#H=nx.krackhardt_kite_graph() +#H=nx.Graph();H.add_edge('a','b');H.add_edge('a','c');H.add_edge('a','d') +#H=nx.grid_2d_graph(4,5) +H=nx.cycle_graph(20) + +# reorder nodes from 0,len(G)-1 +G=nx.convert_node_labels_to_integers(H) +# 3d spring layout +pos=nx.spring_layout(G,dim=3) +# numpy array of x,y,z positions in sorted node order +xyz=np.array([pos[v] for v in sorted(G)]) +# scalar colors +scalars=np.array(G.nodes())+5 + +mlab.figure(1, bgcolor=(0, 0, 0)) +mlab.clf() + +pts = mlab.points3d(xyz[:,0], xyz[:,1], xyz[:,2], + scalars, + scale_factor=0.1, + scale_mode='none', + colormap='Blues', + resolution=20) + +pts.mlab_source.dataset.lines = np.array(G.edges()) +tube = mlab.pipeline.tube(pts, tube_radius=0.01) +mlab.pipeline.surface(tube, color=(0.8, 0.8, 0.8)) + +mlab.savefig('mayavi2_spring.png') +# mlab.show() # interactive window diff --git a/share/doc/networkx-1.11/examples/advanced/eigenvalues.py b/share/doc/networkx-1.11/examples/advanced/eigenvalues.py new file mode 100644 index 000000000..dffa429ac --- /dev/null +++ b/share/doc/networkx-1.11/examples/advanced/eigenvalues.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +""" +Create an G{n,m} random graph and compute the eigenvalues. +Requires numpy and matplotlib. +""" +import networkx as nx +import numpy.linalg +import matplotlib.pyplot as plt + +n = 1000 # 1000 nodes +m = 5000 # 5000 edges +G = nx.gnm_random_graph(n,m) + +L = nx.normalized_laplacian_matrix(G) +e = numpy.linalg.eigvals(L.A) +print("Largest eigenvalue:", max(e)) +print("Smallest eigenvalue:", min(e)) +plt.hist(e,bins=100) # histogram with 100 bins +plt.xlim(0,2) # eigenvalues between 0 and 2 +plt.show() diff --git a/share/doc/networkx-1.11/examples/advanced/heavy_metal_umlaut.py b/share/doc/networkx-1.11/examples/advanced/heavy_metal_umlaut.py new file mode 100644 index 000000000..9055771e2 --- /dev/null +++ b/share/doc/networkx-1.11/examples/advanced/heavy_metal_umlaut.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Example using unicode strings as graph labels. + +Also shows creative use of the Heavy Metal Umlaut: +http://en.wikipedia.org/wiki/Heavy_metal_umlaut + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2006-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as NX +try: + import pylab as P +except ImportError: + pass + +try: + hd='H' + unichr(252) + 'sker D' + unichr(252) + mh='Mot' + unichr(246) + 'rhead' + mc='M' + unichr(246) + 'tley Cr' + unichr(252) + 'e' + st='Sp' + unichr(305) + 'n' + unichr(776) + 'al Tap' + q='Queensr' + unichr(255) + 'che' + boc='Blue ' + unichr(214) +'yster Cult' + dt='Deatht' + unichr(246) + 'ngue' +except NameError: + hd='H' + chr(252) + 'sker D' + chr(252) + mh='Mot' + chr(246) + 'rhead' + mc='M' + chr(246) + 'tley Cr' + chr(252) + 'e' + st='Sp' + chr(305) + 'n' + chr(776) + 'al Tap' + q='Queensr' + chr(255) + 'che' + boc='Blue ' + chr(214) +'yster Cult' + dt='Deatht' + chr(246) + 'ngue' + +G=NX.Graph() +G.add_edge(hd,mh) +G.add_edge(mc,st) +G.add_edge(boc,mc) +G.add_edge(boc,dt) +G.add_edge(st,dt) +G.add_edge(q,st) +G.add_edge(dt,mh) +G.add_edge(st,mh) + +# write in UTF-8 encoding +fh=open('edgelist.utf-8','wb') +fh.write('# -*- coding: utf-8 -*-\n'.encode('utf-8')) # encoding hint for emacs +NX.write_multiline_adjlist(G,fh,delimiter='\t', encoding = 'utf-8') + +# read and store in UTF-8 +fh=open('edgelist.utf-8','rb') +H=NX.read_multiline_adjlist(fh,delimiter='\t', encoding = 'utf-8') + +for n in G.nodes(): + if n not in H: + print(False) + +print(G.nodes()) + +try: + pos=NX.spring_layout(G) + NX.draw(G,pos,font_size=16,with_labels=False) + for p in pos: # raise text positions + pos[p][1]+=0.07 + NX.draw_networkx_labels(G,pos) + P.show() +except: + pass + + diff --git a/share/doc/networkx-1.11/examples/advanced/iterated_dynamical_systems.py b/share/doc/networkx-1.11/examples/advanced/iterated_dynamical_systems.py new file mode 100644 index 000000000..2355b24ac --- /dev/null +++ b/share/doc/networkx-1.11/examples/advanced/iterated_dynamical_systems.py @@ -0,0 +1,199 @@ +""" +Digraphs from Integer-valued Iterated Functions +=============================================== + + +Sums of cubes on 3N +------------------- + +The number 153 has a curious property. + +Let 3N={3,6,9,12,...} be the set of positive multiples of 3. Define an +iterative process f:3N->3N as follows: for a given n, take each digit +of n (in base 10), cube it and then sum the cubes to obtain f(n). + +When this process is repeated, the resulting series n, f(n), f(f(n)),... +terminate in 153 after a finite number of iterations (the process ends +because 153 = 1**3 + 5**3 + 3**3). + +In the language of discrete dynamical systems, 153 is the global +attractor for the iterated map f restricted to the set 3N. + +For example: take the number 108 + +f(108) = 1**3 + 0**3 + 8**3 = 513 + +and + +f(513) = 5**3 + 1**3 + 3**3 = 153 + +So, starting at 108 we reach 153 in two iterations, +represented as: + +108->513->153 + +Computing all orbits of 3N up to 10**5 reveals that the attractor +153 is reached in a maximum of 14 iterations. In this code we +show that 13 cycles is the maximum required for all integers (in 3N) +less than 10,000. + +The smallest number that requires 13 iterations to reach 153, is 177, i.e., + +177->687->1071->345->216->225->141->66->432->99->1458->702->351->153 + +The resulting large digraphs are useful for testing network software. + +The general problem +------------------- + +Given numbers n, a power p and base b, define F(n; p, b) as the sum of +the digits of n (in base b) raised to the power p. The above example +corresponds to f(n)=F(n; 3,10), and below F(n; p, b) is implemented as +the function powersum(n,p,b). The iterative dynamical system defined by +the mapping n:->f(n) above (over 3N) converges to a single fixed point; +153. Applying the map to all positive integers N, leads to a discrete +dynamical process with 5 fixed points: 1, 153, 370, 371, 407. Modulo 3 +those numbers are 1, 0, 1, 2, 2. The function f above has the added +property that it maps a multiple of 3 to another multiple of 3; i.e. it +is invariant on the subset 3N. + + +The squaring of digits (in base 10) result in cycles and the +single fixed point 1. I.e., from a certain point on, the process +starts repeating itself. + +keywords: "Recurring Digital Invariant", "Narcissistic Number", +"Happy Number" + +The 3n+1 problem +---------------- + +There is a rich history of mathematical recreations +associated with discrete dynamical systems. The most famous +is the Collatz 3n+1 problem. See the function +collatz_problem_digraph below. The Collatz conjecture +--- that every orbit returrns to the fixed point 1 in finite time +--- is still unproven. Even the great Paul Erdos said "Mathematics +is not yet ready for such problems", and offered $500 +for its solution. + +keywords: "3n+1", "3x+1", "Collatz problem", "Thwaite's conjecture" + + +""" +from networkx import * +from math import * + + +nmax=10000 +p=3 +mach_eps=0.00000000001 + +def digitsrep(n,b=10): + """Return list of digits comprising n represented in base b. + n must be a nonnegative integer""" + + # very inefficient if you only work with base 10 + dlist=[] + if n<=0: + return [0] + maxpow=int(floor( log(n)/log(b) + mach_eps )) + pow=maxpow + while pow>=0: + x=int(floor(n // b**pow)) + dlist.append(x) + n=n-x*b**pow + pow=pow-1 + return dlist + +def powersum(n,p,b=10): + """Return sum of digits of n (in base b) raised to the power p.""" + dlist=digitsrep(n,b) + sum=0 + for k in dlist: + sum+=k**p + return sum + +def attractor153_graph(n,p,multiple=3,b=10): + """Return digraph of iterations of powersum(n,3,10).""" + G=DiGraph() + for k in range(1,n+1): + if k%multiple==0 and k not in G: + k1=k + knext=powersum(k1,p,b) + while k1!=knext: + G.add_edge(k1,knext) + k1=knext + knext=powersum(k1,p,b) + return G + +def squaring_cycle_graph_old(n,b=10): + """Return digraph of iterations of powersum(n,2,10).""" + G=DiGraph() + for k in range(1,n+1): + k1=k + G.add_node(k1) # case k1==knext, at least add node + knext=powersum(k1,2,b) + G.add_edge(k1,knext) + while k1!=knext: # stop if fixed point + k1=knext + knext=powersum(k1,2,b) + G.add_edge(k1,knext) + if G.out_degree(knext) >=1: + # knext has already been iterated in and out + break + return G + +def sum_of_digits_graph(nmax,b=10): + def f(n): return powersum(n,1,b) + return discrete_dynamics_digraph(nmax,f) + +def squaring_cycle_digraph(nmax,b=10): + def f(n): return powersum(n,2,b) + return discrete_dynamics_digraph(nmax,f) + +def cubing_153_digraph(nmax): + def f(n): return powersum(n,3,10) + return discrete_dynamics_digraph(nmax,f) + +def discrete_dynamics_digraph(nmax,f,itermax=50000): + G=DiGraph() + for k in range(1,nmax+1): + kold=k + G.add_node(kold) + knew=f(kold) + G.add_edge(kold,knew) + while kold!=knew and kold<=1: + # knew has already been iterated in and out + break + return G + +def collatz_problem_digraph(nmax): + def f(n): + if n%2==0: + return n // 2 + else: + return 3*n+1 + return discrete_dynamics_digraph(nmax,f) + +def fixed_points(G): + """Return a list of fixed points for the discrete dynamical + system represented by the digraph G. + """ + return [n for n in G if G.out_degree(n)==0] + + +if __name__ == "__main__": + nmax=10000 + print("Building cubing_153_digraph(%d)"% nmax) + G=cubing_153_digraph(nmax) + print("Resulting digraph has", len(G), "nodes and", + G.size()," edges") + print("Shortest path from 177 to 153 is:") + print(shortest_path(G,177,153)) + print("fixed points are %s" % fixed_points(G)) diff --git a/share/doc/networkx-1.11/examples/advanced/parallel_betweenness.py b/share/doc/networkx-1.11/examples/advanced/parallel_betweenness.py new file mode 100644 index 000000000..45cafd582 --- /dev/null +++ b/share/doc/networkx-1.11/examples/advanced/parallel_betweenness.py @@ -0,0 +1,73 @@ +""" +Example of parallel implementation of betweenness centrality using the +multiprocessing module from Python Standard Library. + +The function betweenness centrality accepts a bunch of nodes and computes +the contribution of those nodes to the betweenness centrality of the whole +network. Here we divide the network in chunks of nodes and we compute their +contribution to the betweenness centrality of the whole network. +""" + +from multiprocessing import Pool +import time +import itertools +import networkx as nx + + +def chunks(l, n): + """Divide a list of nodes `l` in `n` chunks""" + l_c = iter(l) + while 1: + x = tuple(itertools.islice(l_c, n)) + if not x: + return + yield x + + +def _betmap(G_normalized_weight_sources_tuple): + """Pool for multiprocess only accepts functions with one argument. + This function uses a tuple as its only argument. We use a named tuple for + python 3 compatibility, and then unpack it when we send it to + `betweenness_centrality_source` + """ + return nx.betweenness_centrality_source(*G_normalized_weight_sources_tuple) + + +def betweenness_centrality_parallel(G, processes=None): + """Parallel betweenness centrality function""" + p = Pool(processes=processes) + node_divisor = len(p._pool)*4 + node_chunks = list(chunks(G.nodes(), int(G.order()/node_divisor))) + num_chunks = len(node_chunks) + bt_sc = p.map(_betmap, + zip([G]*num_chunks, + [True]*num_chunks, + [None]*num_chunks, + node_chunks)) + + # Reduce the partial solutions + bt_c = bt_sc[0] + for bt in bt_sc[1:]: + for n in bt: + bt_c[n] += bt[n] + return bt_c + +if __name__ == "__main__": + G_ba = nx.barabasi_albert_graph(1000, 3) + G_er = nx.gnp_random_graph(1000, 0.01) + G_ws = nx.connected_watts_strogatz_graph(1000, 4, 0.1) + for G in [G_ba, G_er, G_ws]: + print("") + print("Computing betweenness centrality for:") + print(nx.info(G)) + print("\tParallel version") + start = time.time() + bt = betweenness_centrality_parallel(G) + print("\t\tTime: %.4F" % (time.time()-start)) + print("\t\tBetweenness centrality for node 0: %.5f" % (bt[0])) + print("\tNon-Parallel version") + start = time.time() + bt = nx.betweenness_centrality(G) + print("\t\tTime: %.4F seconds" % (time.time()-start)) + print("\t\tBetweenness centrality for node 0: %.5f" % (bt[0])) + print("") diff --git a/share/doc/networkx-1.11/examples/algorithms/blockmodel.py b/share/doc/networkx-1.11/examples/algorithms/blockmodel.py new file mode 100644 index 000000000..2fed34764 --- /dev/null +++ b/share/doc/networkx-1.11/examples/algorithms/blockmodel.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +Example of creating a block model using the blockmodel function in NX. Data used is the Hartford, CT drug users network: + +@article{, + title = {Social Networks of Drug Users in {High-Risk} Sites: Finding the Connections}, + volume = {6}, + shorttitle = {Social Networks of Drug Users in {High-Risk} Sites}, + url = {http://dx.doi.org/10.1023/A:1015457400897}, + doi = {10.1023/A:1015457400897}, + number = {2}, + journal = {{AIDS} and Behavior}, + author = {Margaret R. Weeks and Scott Clair and Stephen P. Borgatti and Kim Radda and Jean J. Schensul}, + month = jun, + year = {2002}, + pages = {193--206} +} + +""" +# Authors: Drew Conway , Aric Hagberg + +from collections import defaultdict +import networkx as nx +import numpy +from scipy.cluster import hierarchy +from scipy.spatial import distance +import matplotlib.pyplot as plt + + +def create_hc(G): + """Creates hierarchical cluster of graph G from distance matrix""" + path_length=nx.all_pairs_shortest_path_length(G) + distances=numpy.zeros((len(G),len(G))) + for u,p in path_length.items(): + for v,d in p.items(): + distances[u][v]=d + # Create hierarchical cluster + Y=distance.squareform(distances) + Z=hierarchy.complete(Y) # Creates HC using farthest point linkage + # This partition selection is arbitrary, for illustrive purposes + membership=list(hierarchy.fcluster(Z,t=1.15)) + # Create collection of lists for blockmodel + partition=defaultdict(list) + for n,p in zip(list(range(len(G))),membership): + partition[p].append(n) + return list(partition.values()) + +if __name__ == '__main__': + G=nx.read_edgelist("hartford_drug.edgelist") + + # Extract largest connected component into graph H + H=nx.connected_component_subgraphs(G)[0] + # Makes life easier to have consecutively labeled integer nodes + H=nx.convert_node_labels_to_integers(H) + # Create parititions with hierarchical clustering + partitions=create_hc(H) + # Build blockmodel graph + BM=nx.blockmodel(H,partitions) + + + # Draw original graph + pos=nx.spring_layout(H,iterations=100) + fig=plt.figure(1,figsize=(6,10)) + ax=fig.add_subplot(211) + nx.draw(H,pos,with_labels=False,node_size=10) + plt.xlim(0,1) + plt.ylim(0,1) + + # Draw block model with weighted edges and nodes sized by number of internal nodes + node_size=[BM.node[x]['nnodes']*10 for x in BM.nodes()] + edge_width=[(2*d['weight']) for (u,v,d) in BM.edges(data=True)] + # Set positions to mean of positions of internal nodes from original graph + posBM={} + for n in BM: + xy=numpy.array([pos[u] for u in BM.node[n]['graph']]) + posBM[n]=xy.mean(axis=0) + ax=fig.add_subplot(212) + nx.draw(BM,posBM,node_size=node_size,width=edge_width,with_labels=False) + plt.xlim(0,1) + plt.ylim(0,1) + plt.axis('off') + plt.savefig('hartford_drug_block_model.png') diff --git a/share/doc/networkx-1.11/examples/algorithms/davis_club.py b/share/doc/networkx-1.11/examples/algorithms/davis_club.py new file mode 100644 index 000000000..68fab4bf2 --- /dev/null +++ b/share/doc/networkx-1.11/examples/algorithms/davis_club.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +""" +Davis Southern Club Women + +Shows how to make unipartite projections of the graph and compute the +properties of those graphs. + +These data were collected by Davis et al. in the 1930s. +They represent observed attendance at 14 social events by 18 Southern women. +The graph is bipartite (clubs, women). +""" +import networkx as nx +import networkx.algorithms.bipartite as bipartite + +G = nx.davis_southern_women_graph() +women = G.graph['top'] +clubs = G.graph['bottom'] + +print("Biadjacency matrix") +print(bipartite.biadjacency_matrix(G,women,clubs)) + +# project bipartite graph onto women nodes +W = bipartite.projected_graph(G, women) +print('') +print("#Friends, Member") +for w in women: + print('%d %s' % (W.degree(w),w)) + +# project bipartite graph onto women nodes keeping number of co-occurence +# the degree computed is weighted and counts the total number of shared contacts +W = bipartite.weighted_projected_graph(G, women) +print('') +print("#Friend meetings, Member") +for w in women: + print('%d %s' % (W.degree(w,weight='weight'),w)) + diff --git a/share/doc/networkx-1.11/examples/algorithms/hartford_drug.edgelist b/share/doc/networkx-1.11/examples/algorithms/hartford_drug.edgelist new file mode 100644 index 000000000..c1e92c8eb --- /dev/null +++ b/share/doc/networkx-1.11/examples/algorithms/hartford_drug.edgelist @@ -0,0 +1,338 @@ +# source target +1 2 +1 10 +2 1 +2 10 +3 7 +4 7 +4 209 +5 132 +6 150 +7 3 +7 4 +7 9 +8 106 +8 115 +9 1 +9 2 +9 7 +10 1 +10 2 +11 133 +11 218 +12 88 +13 214 +14 24 +14 52 +16 10 +16 19 +17 64 +17 78 +18 55 +18 103 +18 163 +19 18 +20 64 +20 180 +21 16 +21 22 +22 21 +22 64 +22 106 +23 20 +23 22 +23 64 +24 14 +24 31 +24 122 +27 115 +28 29 +29 28 +30 19 +31 24 +31 32 +31 122 +31 147 +31 233 +32 31 +32 86 +34 35 +34 37 +35 34 +35 43 +36 132 +36 187 +37 38 +37 90 +37 282 +38 42 +38 43 +38 210 +40 20 +42 15 +42 38 +43 34 +43 35 +43 38 +45 107 +46 61 +46 72 +48 23 +49 30 +49 64 +49 108 +49 115 +49 243 +50 30 +50 47 +50 55 +50 125 +50 163 +52 218 +52 224 +54 111 +54 210 +55 65 +55 67 +55 105 +55 108 +55 222 +56 18 +56 64 +57 65 +57 125 +58 20 +58 30 +58 50 +58 103 +58 180 +59 164 +63 125 +64 8 +64 50 +64 70 +64 256 +66 20 +66 84 +66 106 +66 125 +67 22 +67 50 +67 113 +68 50 +70 50 +70 64 +71 72 +74 29 +74 75 +74 215 +75 74 +75 215 +76 58 +76 104 +77 103 +78 64 +78 68 +80 207 +80 210 +82 8 +82 77 +82 83 +82 97 +82 163 +83 82 +83 226 +83 243 +84 29 +84 154 +87 101 +87 189 +89 90 +90 89 +90 94 +91 86 +92 19 +92 30 +92 106 +94 72 +94 89 +94 90 +95 30 +96 75 +96 256 +97 80 +97 128 +98 86 +100 86 +101 87 +103 77 +103 104 +104 58 +104 77 +104 103 +106 22 +107 38 +107 114 +107 122 +108 49 +108 55 +111 121 +111 128 +111 210 +113 253 +114 107 +116 30 +116 140 +118 129 +118 138 +120 88 +121 128 +122 31 +123 32 +124 244 +125 132 +126 163 +126 180 +128 38 +128 111 +129 118 +132 29 +132 30 +133 30 +134 135 +134 150 +135 134 +137 144 +138 118 +138 129 +139 142 +141 157 +141 163 +142 139 +143 2 +144 137 +145 151 +146 137 +146 165 +146 169 +146 171 +147 31 +147 128 +148 146 +148 169 +148 171 +148 282 +149 128 +149 148 +149 172 +150 86 +151 145 +152 4 +153 134 +154 155 +156 161 +157 141 +161 156 +165 144 +165 148 +167 149 +169 15 +169 148 +169 171 +170 115 +170 173 +170 183 +170 202 +171 72 +171 148 +171 169 +173 170 +175 100 +176 10 +178 181 +181 178 +182 38 +182 171 +183 96 +185 50 +186 127 +187 50 +187 65 +188 30 +188 50 +189 87 +189 89 +190 35 +190 38 +190 122 +190 182 +191 54 +191 118 +191 129 +191 172 +192 149 +192 167 +195 75 +197 50 +197 188 +198 218 +198 221 +198 222 +200 65 +200 220 +201 113 +202 156 +203 232 +204 194 +207 38 +207 122 +207 124 +208 30 +208 50 +210 38 +210 207 +211 37 +213 35 +213 38 +214 13 +214 14 +214 171 +214 213 +215 75 +217 39 +218 68 +218 222 +221 198 +222 198 +222 218 +223 39 +225 3 +226 22 +229 65 +230 68 +231 43 +232 95 +232 203 +233 99 +234 68 +234 230 +237 244 +238 145 +242 3 +242 113 +244 237 +249 96 +250 156 +252 65 +254 65 +258 113 +268 4 +270 183 +272 6 +275 96 +280 183 +280 206 +282 37 +285 75 +290 285 +293 290 \ No newline at end of file diff --git a/share/doc/networkx-1.11/examples/algorithms/krackhardt_centrality.py b/share/doc/networkx-1.11/examples/algorithms/krackhardt_centrality.py new file mode 100644 index 000000000..71dee8f18 --- /dev/null +++ b/share/doc/networkx-1.11/examples/algorithms/krackhardt_centrality.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +""" +Centrality measures of Krackhardt social network. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) +# Date: 2005-05-12 14:33:11 -0600 (Thu, 12 May 2005) +# Revision: 998 + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +from networkx import * + +G=krackhardt_kite_graph() + +print("Betweenness") +b=betweenness_centrality(G) +for v in G.nodes(): + print("%0.2d %5.3f"%(v,b[v])) + +print("Degree centrality") +d=degree_centrality(G) +for v in G.nodes(): + print("%0.2d %5.3f"%(v,d[v])) + +print("Closeness centrality") +c=closeness_centrality(G) +for v in G.nodes(): + print("%0.2d %5.3f"%(v,c[v])) diff --git a/share/doc/networkx-1.11/examples/algorithms/rcm.py b/share/doc/networkx-1.11/examples/algorithms/rcm.py new file mode 100644 index 000000000..43fa56f6c --- /dev/null +++ b/share/doc/networkx-1.11/examples/algorithms/rcm.py @@ -0,0 +1,32 @@ +# Cuthill-McKee ordering of matrices +# The reverse Cuthill-McKee algorithm gives a sparse matrix ordering that +# reduces the matrix bandwidth. +# Requires NumPy +# Copyright (C) 2011-2016 by +# Author: Aric Hagberg +# BSD License +import networkx as nx +from networkx.utils import reverse_cuthill_mckee_ordering +import numpy as np + +# build low-bandwidth numpy matrix +G=nx.grid_2d_graph(3,3) +rcm = list(reverse_cuthill_mckee_ordering(G)) +print("ordering",rcm) + +print("unordered Laplacian matrix") +A = nx.laplacian_matrix(G) +x,y = np.nonzero(A) +#print("lower bandwidth:",(y-x).max()) +#print("upper bandwidth:",(x-y).max()) +print("bandwidth: %d"%((y-x).max()+(x-y).max()+1)) +print(A) + +B = nx.laplacian_matrix(G,nodelist=rcm) +print("low-bandwidth Laplacian matrix") +x,y = np.nonzero(B) +#print("lower bandwidth:",(y-x).max()) +#print("upper bandwidth:",(x-y).max()) +print("bandwidth: %d"%((y-x).max()+(x-y).max()+1)) +print(B) + diff --git a/share/doc/networkx-1.11/examples/basic/properties.py b/share/doc/networkx-1.11/examples/basic/properties.py new file mode 100644 index 000000000..9ac87f86e --- /dev/null +++ b/share/doc/networkx-1.11/examples/basic/properties.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +""" +Compute some network properties for the lollipop graph. +""" +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +from networkx import * + +G = lollipop_graph(4,6) + +pathlengths=[] + +print("source vertex {target:length, }") +for v in G.nodes(): + spl=single_source_shortest_path_length(G,v) + print('%s %s' % (v,spl)) + for p in spl.values(): + pathlengths.append(p) + +print('') +print("average shortest path length %s" % (sum(pathlengths)/len(pathlengths))) + +# histogram of path lengths +dist={} +for p in pathlengths: + if p in dist: + dist[p]+=1 + else: + dist[p]=1 + +print('') +print("length #paths") +verts=dist.keys() +for d in sorted(verts): + print('%s %d' % (d,dist[d])) + +print("radius: %d" % radius(G)) +print("diameter: %d" % diameter(G)) +print("eccentricity: %s" % eccentricity(G)) +print("center: %s" % center(G)) +print("periphery: %s" % periphery(G)) +print("density: %s" % density(G)) + diff --git a/share/doc/networkx-1.11/examples/basic/read_write.py b/share/doc/networkx-1.11/examples/basic/read_write.py new file mode 100644 index 000000000..37db0cd7a --- /dev/null +++ b/share/doc/networkx-1.11/examples/basic/read_write.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +""" +Read and write graphs. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +from networkx import * +import sys +G=grid_2d_graph(5,5) # 5x5 grid +try: # Python 2.6+ + write_adjlist(G,sys.stdout) # write adjacency list to screen +except TypeError: # Python 3.x + write_adjlist(G,sys.stdout.buffer) # write adjacency list to screen +# write edgelist to grid.edgelist +write_edgelist(G,path="grid.edgelist",delimiter=":") +# read edgelist from grid.edgelist +H=read_edgelist(path="grid.edgelist",delimiter=":") + diff --git a/share/doc/networkx-1.11/examples/drawing/atlas.py b/share/doc/networkx-1.11/examples/drawing/atlas.py new file mode 100644 index 000000000..418ac6363 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/atlas.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +""" +Atlas of all graphs of 6 nodes or less. + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx +from networkx.generators.atlas import * +from networkx.algorithms.isomorphism.isomorph import graph_could_be_isomorphic as isomorphic +import random + +def atlas6(): + """ Return the atlas of all connected graphs of 6 nodes or less. + Attempt to check for isomorphisms and remove. + """ + + Atlas = graph_atlas_g()[0:208] # 208 + # remove isolated nodes, only connected graphs are left + U = nx.Graph() # graph for union of all graphs in atlas + for G in Atlas: + zerodegree = [n for n in G if G.degree(n)==0] + for n in zerodegree: + G.remove_node(n) + U = nx.disjoint_union(U, G) + + # list of graphs of all connected components + C = nx.connected_component_subgraphs(U) + + UU = nx.Graph() + # do quick isomorphic-like check, not a true isomorphism checker + nlist = [] # list of nonisomorphic graphs + for G in C: + # check against all nonisomorphic graphs so far + if not iso(G, nlist): + nlist.append(G) + UU = nx.disjoint_union(UU, G) # union the nonisomorphic graphs + return UU + +def iso(G1, glist): + """Quick and dirty nonisomorphism checker used to check isomorphisms.""" + for G2 in glist: + if isomorphic(G1, G2): + return True + return False + + +if __name__ == '__main__': + G=atlas6() + + print("graph has %d nodes with %d edges"\ + %(nx.number_of_nodes(G), nx.number_of_edges(G))) + print(nx.number_connected_components(G), "connected components") + + try: + import pygraphviz + from networkx.drawing.nx_agraph import graphviz_layout + except ImportError: + try: + import pydotplus + from networkx.drawing.nx_pydot import graphviz_layout + except ImportError: + raise ImportError("This example needs Graphviz and either " + "PyGraphviz or PyDotPlus") + + import matplotlib.pyplot as plt + plt.figure(1, figsize=(8, 8)) + # layout graphs with positions using graphviz neato + pos = graphviz_layout(G, prog="neato") + # color nodes the same in each connected subgraph + C = nx.connected_component_subgraphs(G) + for g in C: + c = [random.random()] * nx.number_of_nodes(g) # random color... + nx.draw(g, + pos, + node_size=40, + node_color=c, + vmin=0.0, + vmax=1.0, + with_labels=False + ) + plt.savefig("atlas.png", dpi=75) diff --git a/share/doc/networkx-1.11/examples/drawing/chess_masters.py b/share/doc/networkx-1.11/examples/drawing/chess_masters.py new file mode 100644 index 000000000..76e8ae6f7 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/chess_masters.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python + +""" +An example of the MultiDiGraph clas + +The function chess_pgn_graph reads a collection of chess +matches stored in the specified PGN file +(PGN ="Portable Game Notation") +Here the (compressed) default file --- + chess_masters_WCC.pgn.bz2 --- +contains all 685 World Chess Championship matches +from 1886 - 1985. +(data from http://chessproblem.my-free-games.com/chess/games/Download-PGN.php) + +The chess_pgn_graph() function returns a MultiDiGraph +with multiple edges. Each node is +the last name of a chess master. Each edge is directed +from white to black and contains selected game info. + +The key statement in chess_pgn_graph below is + G.add_edge(white, black, game_info) +where game_info is a dict describing each game. + +""" +# Copyright (C) 2006-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx + +# tag names specifying what game info should be +# stored in the dict on each digraph edge +game_details=["Event", + "Date", + "Result", + "ECO", + "Site"] + +def chess_pgn_graph(pgn_file="chess_masters_WCC.pgn.bz2"): + """Read chess games in pgn format in pgn_file. + + Filenames ending in .gz or .bz2 will be uncompressed. + + Return the MultiDiGraph of players connected by a chess game. + Edges contain game data in a dict. + + """ + import bz2 + G=nx.MultiDiGraph() + game={} + datafile = bz2.BZ2File(pgn_file) + lines = (line.decode().rstrip('\r\n') for line in datafile) + for line in lines: + if line.startswith('['): + tag,value=line[1:-1].split(' ',1) + game[str(tag)]=value.strip('"') + else: + # empty line after tag set indicates + # we finished reading game info + if game: + white=game.pop('White') + black=game.pop('Black') + G.add_edge(white, black, **game) + game={} + return G + + +if __name__ == '__main__': + G=chess_pgn_graph() + + ngames=G.number_of_edges() + nplayers=G.number_of_nodes() + + print("Loaded %d chess games between %d players\n"\ + % (ngames,nplayers)) + + # identify connected components + # of the undirected version + Gcc=list(nx.connected_component_subgraphs(G.to_undirected())) + if len(Gcc)>1: + print("Note the disconnected component consisting of:") + print(Gcc[1].nodes()) + + # find all games with B97 opening (as described in ECO) + openings=set([game_info['ECO'] + for (white,black,game_info) in G.edges(data=True)]) + print("\nFrom a total of %d different openings,"%len(openings)) + print('the following games used the Sicilian opening') + print('with the Najdorff 7...Qb6 "Poisoned Pawn" variation.\n') + + for (white,black,game_info) in G.edges(data=True): + if game_info['ECO']=='B97': + print(white,"vs",black) + for k,v in game_info.items(): + print(" ",k,": ",v) + print("\n") + + + try: + import matplotlib.pyplot as plt + except ImportError: + import sys + print("Matplotlib needed for drawing. Skipping") + sys.exit(0) + + # make new undirected graph H without multi-edges + H=nx.Graph(G) + + # edge width is proportional number of games played + edgewidth=[] + for (u,v,d) in H.edges(data=True): + edgewidth.append(len(G.get_edge_data(u,v))) + + # node size is proportional to number of games won + wins=dict.fromkeys(G.nodes(),0.0) + for (u,v,d) in G.edges(data=True): + r=d['Result'].split('-') + if r[0]=='1': + wins[u]+=1.0 + elif r[0]=='1/2': + wins[u]+=0.5 + wins[v]+=0.5 + else: + wins[v]+=1.0 + try: + pos=nx.nx_agraph.graphviz_layout(H) + except: + pos=nx.spring_layout(H,iterations=20) + + plt.rcParams['text.usetex'] = False + plt.figure(figsize=(8,8)) + nx.draw_networkx_edges(H,pos,alpha=0.3,width=edgewidth, edge_color='m') + nodesize=[wins[v]*50 for v in H] + nx.draw_networkx_nodes(H,pos,node_size=nodesize,node_color='w',alpha=0.4) + nx.draw_networkx_edges(H,pos,alpha=0.4,node_size=0,width=1,edge_color='k') + nx.draw_networkx_labels(H,pos,fontsize=14) + font = {'fontname' : 'Helvetica', + 'color' : 'k', + 'fontweight' : 'bold', + 'fontsize' : 14} + plt.title("World Chess Championship Games: 1886 - 1985", font) + + # change font and write text (using data coordinates) + font = {'fontname' : 'Helvetica', + 'color' : 'r', + 'fontweight' : 'bold', + 'fontsize' : 14} + + plt.text(0.5, 0.97, "edge width = # games played", + horizontalalignment='center', + transform=plt.gca().transAxes) + plt.text(0.5, 0.94, "node size = # games won", + horizontalalignment='center', + transform=plt.gca().transAxes) + + plt.axis('off') + plt.savefig("chess_masters.png",dpi=75) + print("Wrote chess_masters.png") + plt.show() # display diff --git a/share/doc/networkx-1.11/examples/drawing/chess_masters_WCC.pgn.bz2 b/share/doc/networkx-1.11/examples/drawing/chess_masters_WCC.pgn.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..3761ce5280c7a4906b04fab66d422cf92ab567be GIT binary patch literal 100224 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 BrKJD> literal 0 HcmV?d00001 diff --git a/share/doc/networkx-1.11/examples/drawing/circular_tree.py b/share/doc/networkx-1.11/examples/drawing/circular_tree.py new file mode 100644 index 000000000..77216bd76 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/circular_tree.py @@ -0,0 +1,22 @@ +import networkx as nx +import matplotlib.pyplot as plt + +try: + import pygraphviz + from networkx.drawing.nx_agraph import graphviz_layout +except ImportError: + try: + import pydotplus + from networkx.drawing.nx_pydot import graphviz_layout + except ImportError: + raise ImportError("This example needs Graphviz and either " + "PyGraphviz or PyDotPlus") + +G = nx.balanced_tree(3, 5) +pos = graphviz_layout(G, prog='twopi', args='') +plt.figure(figsize=(8, 8)) +nx.draw(G, pos, node_size=20, alpha=0.5, node_color="blue", with_labels=False) +plt.axis('equal') +plt.savefig('circular_tree.png') +plt.show() + diff --git a/share/doc/networkx-1.11/examples/drawing/degree_histogram.py b/share/doc/networkx-1.11/examples/drawing/degree_histogram.py new file mode 100644 index 000000000..a6dd8742c --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/degree_histogram.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +""" +Random graph from given degree sequence. +Draw degree rank plot and graph with matplotlib. +""" +# Author: Aric Hagberg +import matplotlib.pyplot as plt +import networkx as nx + +G = nx.gnp_random_graph(100,0.02) + +degree_sequence=sorted(nx.degree(G).values(),reverse=True) # degree sequence +#print "Degree sequence", degree_sequence +dmax=max(degree_sequence) + +plt.loglog(degree_sequence,'b-',marker='o') +plt.title("Degree rank plot") +plt.ylabel("degree") +plt.xlabel("rank") + +# draw graph in inset +plt.axes([0.45,0.45,0.45,0.45]) +Gcc=sorted(nx.connected_component_subgraphs(G), key = len, reverse=True)[0] +pos=nx.spring_layout(Gcc) +plt.axis('off') +nx.draw_networkx_nodes(Gcc,pos,node_size=20) +nx.draw_networkx_edges(Gcc,pos,alpha=0.4) + +plt.savefig("degree_histogram.png") +plt.show() + diff --git a/share/doc/networkx-1.11/examples/drawing/edge_colormap.py b/share/doc/networkx-1.11/examples/drawing/edge_colormap.py new file mode 100644 index 000000000..a2aa7a3a4 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/edge_colormap.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +""" +Draw a graph with matplotlib, color edges. +You must have matplotlib>=87.7 for this to work. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) +try: + import matplotlib.pyplot as plt +except: + raise + +import networkx as nx + +G=nx.star_graph(20) +pos=nx.spring_layout(G) +colors=range(20) +nx.draw(G,pos,node_color='#A0CBE2',edge_color=colors,width=4,edge_cmap=plt.cm.Blues,with_labels=False) +plt.savefig("edge_colormap.png") # save as png +plt.show() # display diff --git a/share/doc/networkx-1.11/examples/drawing/ego_graph.py b/share/doc/networkx-1.11/examples/drawing/ego_graph.py new file mode 100644 index 000000000..e1092738a --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/ego_graph.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Example using the NetworkX ego_graph() function to return the main egonet of +the largest hub in a Barabási-Albert network. +""" +# Author: Drew Conway (drew.conway@nyu.edu) + +from operator import itemgetter +import networkx as nx +import matplotlib.pyplot as plt + + +if __name__ == '__main__': + # Create a BA model graph + n=1000 + m=2 + G=nx.generators.barabasi_albert_graph(n,m) + # find node with largest degree + node_and_degree=G.degree() + (largest_hub,degree)=sorted(node_and_degree.items(),key=itemgetter(1))[-1] + # Create ego graph of main hub + hub_ego=nx.ego_graph(G,largest_hub) + # Draw graph + pos=nx.spring_layout(hub_ego) + nx.draw(hub_ego,pos,node_color='b',node_size=50,with_labels=False) + # Draw ego as large and red + nx.draw_networkx_nodes(hub_ego,pos,nodelist=[largest_hub],node_size=300,node_color='r') + plt.savefig('ego_graph.png') + plt.show() diff --git a/share/doc/networkx-1.11/examples/drawing/four_grids.py b/share/doc/networkx-1.11/examples/drawing/four_grids.py new file mode 100644 index 000000000..112019170 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/four_grids.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +""" +Draw a graph with matplotlib. +You must have matplotlib for this to work. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2004-2016 +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +try: + import matplotlib.pyplot as plt +except: + raise + +import networkx as nx + +G=nx.grid_2d_graph(4,4) #4x4 grid + +pos=nx.spring_layout(G,iterations=100) + +plt.subplot(221) +nx.draw(G,pos,font_size=8) + +plt.subplot(222) +nx.draw(G,pos,node_color='k',node_size=0,with_labels=False) + +plt.subplot(223) +nx.draw(G,pos,node_color='g',node_size=250,with_labels=False,width=6) + +plt.subplot(224) +H=G.to_directed() +nx.draw(H,pos,node_color='b',node_size=20,with_labels=False) + +plt.savefig("four_grids.png") +plt.show() diff --git a/share/doc/networkx-1.11/examples/drawing/giant_component.py b/share/doc/networkx-1.11/examples/drawing/giant_component.py new file mode 100644 index 000000000..9ec3266c1 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/giant_component.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +""" +This example illustrates the sudden appearance of a +giant connected component in a binomial random graph. + +Requires pygraphviz and matplotlib to draw. + +""" +# Copyright (C) 2006-2016 +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +try: + import matplotlib.pyplot as plt +except: + raise + +import networkx as nx +import math + +try: + import pygraphviz + from networkx.drawing.nx_agraph import graphviz_layout + layout = graphviz_layout +except ImportError: + try: + import pydotplus + from networkx.drawing.nx_pydot import graphviz_layout + layout = graphviz_layout + except ImportError: + print("PyGraphviz and PyDotPlus not found;\n" + "drawing with spring layout;\n" + "will be slow.") + layout = nx.spring_layout + + +n=150 # 150 nodes +# p value at which giant component (of size log(n) nodes) is expected +p_giant=1.0/(n-1) +# p value at which graph is expected to become completely connected +p_conn=math.log(n)/float(n) + +# the following range of p values should be close to the threshold +pvals=[0.003, 0.006, 0.008, 0.015] + +region=220 # for pylab 2x2 subplot layout +plt.subplots_adjust(left=0,right=1,bottom=0,top=0.95,wspace=0.01,hspace=0.01) +for p in pvals: + G=nx.binomial_graph(n,p) + pos=layout(G) + region+=1 + plt.subplot(region) + plt.title("p = %6.3f"%(p)) + nx.draw(G,pos, + with_labels=False, + node_size=10 + ) + # identify largest connected component + Gcc=sorted(nx.connected_component_subgraphs(G), key = len, reverse=True) + G0=Gcc[0] + nx.draw_networkx_edges(G0,pos, + with_labels=False, + edge_color='r', + width=6.0 + ) + # show other connected components + for Gi in Gcc[1:]: + if len(Gi)>1: + nx.draw_networkx_edges(Gi,pos, + with_labels=False, + edge_color='r', + alpha=0.3, + width=5.0 + ) +plt.savefig("giant_component.png") +plt.show() # display diff --git a/share/doc/networkx-1.11/examples/drawing/house_with_colors.py b/share/doc/networkx-1.11/examples/drawing/house_with_colors.py new file mode 100644 index 000000000..78aa15219 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/house_with_colors.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +""" +Draw a graph with matplotlib. +You must have matplotlib for this to work. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) +try: + import matplotlib.pyplot as plt +except: + raise + +import networkx as nx + +G=nx.house_graph() +# explicitly set positions +pos={0:(0,0), + 1:(1,0), + 2:(0,1), + 3:(1,1), + 4:(0.5,2.0)} + +nx.draw_networkx_nodes(G,pos,node_size=2000,nodelist=[4]) +nx.draw_networkx_nodes(G,pos,node_size=3000,nodelist=[0,1,2,3],node_color='b') +nx.draw_networkx_edges(G,pos,alpha=0.5,width=6) +plt.axis('off') +plt.savefig("house_with_colors.png") # save as png +plt.show() # display diff --git a/share/doc/networkx-1.11/examples/drawing/knuth_miles.py b/share/doc/networkx-1.11/examples/drawing/knuth_miles.py new file mode 100644 index 000000000..0ba9fd767 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/knuth_miles.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +""" +An example using networkx.Graph(). + +miles_graph() returns an undirected graph over the 128 US cities from +the datafile miles_dat.txt. The cities each have location and population +data. The edges are labeled with the distance betwen the two cities. + +This example is described in Section 1.1 in Knuth's book [1,2]. + +References. +----------- + +[1] Donald E. Knuth, + "The Stanford GraphBase: A Platform for Combinatorial Computing", + ACM Press, New York, 1993. +[2] http://www-cs-faculty.stanford.edu/~knuth/sgb.html + + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx + + +def miles_graph(): + """ Return the cites example graph in miles_dat.txt + from the Stanford GraphBase. + """ + # open file miles_dat.txt.gz (or miles_dat.txt) + import gzip + fh = gzip.open('knuth_miles.txt.gz','r') + + G=nx.Graph() + G.position={} + G.population={} + + cities=[] + for line in fh.readlines(): + line = line.decode() + if line.startswith("*"): # skip comments + continue + + numfind=re.compile("^\d+") + + if numfind.match(line): # this line is distances + dist=line.split() + for d in dist: + G.add_edge(city,cities[i],weight=int(d)) + i=i+1 + else: # this line is a city, position, population + i=1 + (city,coordpop)=line.split("[") + cities.insert(0,city) + (coord,pop)=coordpop.split("]") + (y,x)=coord.split(",") + + G.add_node(city) + # assign position - flip x axis for matplotlib, shift origin + G.position[city]=(-int(x)+7500,int(y)-3000) + G.population[city]=float(pop)/1000.0 + return G + +if __name__ == '__main__': + import networkx as nx + import re + import sys + + G=miles_graph() + + print("Loaded miles_dat.txt containing 128 cities.") + print("digraph has %d nodes with %d edges"\ + %(nx.number_of_nodes(G),nx.number_of_edges(G))) + + + # make new graph of cites, edge if less then 300 miles between them + H=nx.Graph() + for v in G: + H.add_node(v) + for (u,v,d) in G.edges(data=True): + if d['weight'] < 300: + H.add_edge(u,v) + + # draw with matplotlib/pylab + + try: + import matplotlib.pyplot as plt + plt.figure(figsize=(8,8)) + # with nodes colored by degree sized by population + node_color=[float(H.degree(v)) for v in H] + nx.draw(H,G.position, + node_size=[G.population[v] for v in H], + node_color=node_color, + with_labels=False) + + # scale the axes equally + plt.xlim(-5000,500) + plt.ylim(-2000,3500) + + plt.savefig("knuth_miles.png") + except: + pass + + + diff --git a/share/doc/networkx-1.11/examples/drawing/knuth_miles.txt.gz b/share/doc/networkx-1.11/examples/drawing/knuth_miles.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..62b7f95fb8aebea1f93b55924697e2a1958d3b13 GIT binary patch literal 20317 zcmV(vKGJzee~If|ex28h%Y1ly`{955(?9+A@^1DC#@b>*veDZJa761SI^7i>{xYd_$_s`$Fe0+cT*N^u%;%!@1J>t5Ao=Kd3bn^i@f`A|NLz{@!QMg{;w~O?_&jiy1aXN{^rx`XT@$uWo=XjO-w|xKj{r`yH{|is{4=(x7V#)vEc7gxD zXS@8jFaG*L!`ajic*{PpoQo|3EGfBSHW>)l`CW0wz)-+uV< z{-@W=7UvIVdyK%O0Ep;tr z83%v=?(zG0_CNpp>+Lw!yZFmquG{GKSjt}7rEXoOWEssIo56Y^W$sm?rXfuFCU(sUgO%o`Z^w^-Su&v*SgoTl(LOW>E*m^ zy8lS;wWGTfLOp$7LJb?fLof zhlg+D$$s&5JU4Gx_UnFZ+u4?Qz;ZeI5jSh?Qnu~5?E4zuJ_p}?_hG)(pML&z8)e*` zy&TuN)^hUp>!ptM5=(K3wYk(|E0Y=akI&z_wmj`tx4ZKkHJ*0V)|VFF zEWO48mROtk<>fr~xZWPmvahjH?fkoI{PLgU3C{iQ-1q%@t|iulo0g-;|4P4XZN0>n z#}!{$z3g{oZ3j=%LIUC;;yu<_ymFR$2_azR+oks&zi#`b ztVgWT!SeKySG)iI^@o>_Z+Dm9$8MJzPj#;AwY6=Hy^EcXOV!JM#7eBOBzuVk8s*Yk zy|iPnP;s-k_NBJ*`GP#(pa1lAJCAf0L6wym+< z+of-DojumCtY^8jy~P#ZeRvG*{Fi&h>#P6$b&J)$8+{wsu~nWi#4?vTWG7y1KjSXV zS8T}6&OadwWsSR*zOf|z^mZXw+wBf_mw)?`2Oen6b`7EFZC_eETv=BxajvXh{Ab&| z0g@G~J@~IO>gC+#?v*p1U@P2lkIR1ugAg0OIZu67fV=K%YDD7 zhb=a651D6K#ujmHmF*h6$zo+AFX?IRwg$A6w^oEgSZ#!Bv5WW?GL8(TFLgf`hSvG#hH`JLB z+rpx3M_Fpyd))Hj{oUmYgwwml<*&7eP9HTSsA41z0`(ZMWtuy3lgxP7a z)$u#trSI|62wkYvrH+4gmgx*ZsmC5deE-wCk1wwwZT}jUB5oKW+`=-AcE%%(&5N%W zBmi}D^UFGNcYLumRox9GEAlX~sJ!tO8gTBhKWmt~J$CGfB@RK4 z7Y*47R}$9c4AI;|j(RL=C6sj%EQ(O^y7B@d*8!8WRhLm&HGc4p*L#QxD;i4|c zTMIK%PxP>@2eOe*@$~W%JAkL%VD9!ppz-zdU~@IsBH z4OJcLW5p%K?$$6e_pcv5yUK94y$_sHh-M21QJ2Po9^u~H_t2vge|cia2A9HpWA!7t ziJv-V9&1Jrf!Y`KfCUco#$)O+|n$vcsyMXi(S`_9XYvJU;Y&F;luOuLm2&E{Wc_yxaHhqHP_Il z#m|SI)Ak(k9|UJxtWGaPLWkP1$8w&bTA{%wo|LEV#9$k)d%+~)agUQ*V#Gr_J3d_> zfGORU=k31bI0I!AoIziD!CA;fbHl)bZGWg4rVyD&uCI!_)c5 zq0w9@+;cqicdubph}I*r3Te0-YwsbTp|{H(_uE6qYZ|bf z`|#@!@Dp)aM#%UX(t&>XF*VE(mpwqCV{v(=c(EQ5F_enfi3lX7Ssde7{&fHR?&Z^8 zNf3VVMI;xI_JqqU5vj2(;rI`}vKxT8%R82W_?%x6ZN!ITbq4BgP`D4}Anrd1X5($b zKw%5QYJ`&yy+LwT0vN668GB!K>jZ*vjfO^|ZUpWJVSu(R(mDQs@}6!^JX@&1pTZhE z{B-#uQrt`qLQo@44mk-^w2|VlV>Lso3s)~FeLS?ffraD~1Pun$8+l&XsACKx{-PH- zBMAu;hyL(sEXK*6tpt;JDmM+YT2PRPJ=sUz$5=l;zBjIQ`Y_v~^hAuq;;jReH zI(Cbt4^I)6cE_ub=HNqd;2U`y>16z3L!aaMxo{XnEMP&k!w)Tge0zL%KLd$UB9N#p zeE1QbD%NMNcWfudmnH1+PzYkdd57^NFb(T(ykFAP^_D42zFcYeD$_xvhDbUhmaq(% z&Q)bBu^?E&lUS1r#&=^QoBKxc5PBRwc441G+cZHTUcD`Ue13d+&PeF@B*qbqosj`u z<6X#*f)z^NjPVL>i%V1vq(@1BJ zEyT_iQ-uvD7U2MX8<)imV#nDV^#i|m#N&5d((VIp>oSZtxsd*bEh{{C1U2YD#Dlp1 zW*TLviJ5F?rcmjds;h-8ul%&~JAGzoP23Ib2_^pO$H(VeuKO!coc*}NhM(6Oeqs

VvJ1Hyf5Srtk$}d-a253Xh!x{g1KYHBlYn(W*}Ga43W{4iMMY4?@NBMMBo6U5Pi)KY{tYbd z2rU}r8u{J;wrRwrC8MZjT&4lREZ_uq;4lw^*gbOK(0KIAR2%7WF>rbr-ybtqLqgAp ziF)eTky%2x%9e@Mz(Wvh#@~lv3w|R)g+(xo5Ry>1WKtM9kQrz!G!Az^^G=Zu$E&>) zkox)m8x|t6`GS|Yf{nDe*lOOW3h>ivY;$%Jt_mpl#wDb%N(31x)qs3szXU?eO; zI0hoSs@U&D;Dk3BFd@N_&Q^kNRz)vJ^jvrKtY-2qIsqya!Ib%9$xx$efGZ*~%06Q~ z1N0*^+>*;0f)k?o@$vcb{XMYB@4jwliBM(@bABDM|I1D2L#oUvLx{rtXxexF>AdnT ze6^Z6pdZ`JDlyte2Cz$})i|+GIS>cG90JU4VctV{x#(d)Xk>2ey(bWbdo@JZ0Ev%y zm|PgUjC;Eafj_Si8AeP|Q1Z|dtq5{^ ztZGMCYOH%aE2$*#+t^ij{Iu#vZi?pMw2F8L=CJuNxlG}p^Csc_XBedm$O)_vkj?DDHP%w3on$72Kqs439Q@?na@!f9#iq0@P z5oTON)WQ#T94>J|T?J6hW@GQjmxfEZ<;I_gUJ<3?PU4zuE~a;1ML+N{A%i#t(iF1u zFbHBj;#zyA((xG+L4k!WT^^Bb+&mT^gLZ7BuxHBI>7NL-Kq_MUQ76Jufbx2a)+3i) zzIypcP!jg&x0DmexLd#OV*u83=MAAsv#GP_<_Gig@$5%t(84Dsk*jd6i9ObW4LBmR zJF3FI&7dTn3v`U=COr@9f9 z)1U`Si7zIQT2BJ}9&h}^!*>&e4i^#z_&BNS2*3bj^mEOCofN1P^Pn)d$mpqGiK~LZ zY2CLopZnNH+LqAbg)|~`Bl66Ac??dOmWYLFhABAAbJdO?*gc|~Vl;Gi*QzZYtqsEz zS)MXfSnn}1Hs%tm?_XvqEA}{^Z@b3-Lhlz$Ir%*@ zwC~#uw%CA6n>bHK=3<@$%p@cUuYnU6aEYi*Q-gdr6oT|WY_xa*6v1p>l$gz8EGsY3pXwBTG*Io5RcFS9u`7{-AiLfu=X=}rdnk3qXXKuVmXDt zkIzxJs79pj{9`d1a7HY3!4SDn8sWk)9fjb2`qql zVJ~_OD83xNXa##Tkn%{LaE1Dp!0WHjC6N9A-7RJdmLdXon0D{OE&y!&47# zLXM9w=yF&k+z)|2@y~%25PN`9;m(aWRzj~vLPg*bt4B7JNJ^OQ`|p3a&(cgpT6Yn| z?bq1y$PyP_EcJ=>m-?5@{7NK4#J(7J6M}9vBUUo4ysZkRaoI*ZxtfZC?MO~Z^kFZ5_ph(ikz!q1L??HBi*&z(F@s*}EY6Vef$jp;CJOMik?hvCOL0f^(wyZNZ*i*gFqNwCJ8NU&@-)-BXv zSYyFd0=+Kw>^(B?5N$uiNH=mF{&qHz4kN}MKkVjPrwdOCnyYV7ti*!6d%0ES&$?0N z+VJZgf85BpTXn4mu+=L3gj)wZOD{;!FtWl?GnR;S(Y&W-eeCGga@bk|scuoB?P)%W z*j5u;J8}_qtQwEQa$3Q!vDMwj&(d}X-AcR!lG3Okk}DMcLlB*MK^dlh;10sk=iV~< zh-*+2(eQ3oDC<%+=onziqlg>u1>zpN@a0b*@85lTeV9l~ILUCt`;`baa;~aDE1LX* zq5w_Ces10m-~{j7DLa-~YqVt4^nvE_)x{;SCDCC6#ij^f%bG>MP)RViK8s)nb=0E~ zQHI(jTpl_=&1cESs53(#!m6lZJ&e;ZWaXTI0}+&AwT{)bp+LP}Owz5gX|;+zsvbr( z)X!U8zWnk2@%i;H5ilikR*#i><#r8|SQnzdDmc5Z*6XPp8Kk3-?%ld?3E!O}W+_)k z!u@#oQ28(|@f%bGn>K({f!OwVjs_%<#)xolKP*j0w7h9DaRNhIiW;q&mkw}yrVs#K z#ImruwWkdRsxO8R_qM_r|dxY%LLhRXThE0aPLA6=W)>Id#}Ba3YNaAwyhc8`jW|{1D+D zFnc4E-@{b4)zXsD7@^dbSFM!N;T{lg;;}4&r8OX&Vr`2xZ3~74&4V=)cNjIpGf^iQ z0U6rE&Ol7S-Vkjo92l0JTnt@5gr4Flj(z#^?c>X*_Y;_>ptX*17hZN;BOdL`Vccfr z6)7wkz!f+kr@RMibbI;{;6+N?hbh~dkVN>RW2V(sjG9UbqW%oLoafSQkZ(1k=nI&XI098$ zf^hI5(e_hYP=&5zjb7d*lj@7msPIH#kYiJ>z@8@*C~<5J^TlrF{^Kq7U7o&t{blkH@o+ zrm}a6o>sI{xyrkvu@xbyq}l|v6zf97Sb(#nlPnbaDiob~y;ys8I;4QuL1G1&2AW79 zLy$yVN&sxB=uCGLtM~BNhv(PJ?_VCD-zKMF4+V@6_ZqJlGG9SNht(HM85pe5^~fx( zrBjlvf^R>ER0h0+Sr^I<8oNjjp#FwP49w{+r=?}!oTx3TgPj=hkmFWf+QdE(#FK1; zFi9(Am>ai^<F7b;7|dfX5^Nr@{leqTsHX%}`@B&bBIM{gKUew?~2r*dOC9h2p>6l71>IuHr^9z*Ml=N?T^0=*i(@Jc zh!9e`9J`RC=t>pi)wGo}Yu&THMUJc!>9^X>#^38e%I-fULI&{>#^4#Q?HVb{eohS! zn?JxnPH0V7YN{_3y%3#~SY#tYW@q+9M>m6pkn}wg4`J-$+LLGr_l(Ryw2w)_lYRnv zza?j=X)J*(qNp}YNcs;Uu4hAy_nlxflkF6>Fj z`e~(uK!E@$@g$2fqPmDU857S>o-h`2ysfx;6E7il=@D5R(nYQ1AWo#STN15=U)D0Q ziIYlF#fh3!QLDilG%{Hph0IqFJY<<@w(fb6;WzL#Vnq9a|B-hYj=p&{856)Jz`+L; zm~$3Ysq?XaIux`GF~>>&-J1Od%(>RPh)f2&R6@UlJtL!|0y7Q~Kv84WW_+e}v2y$d zLn@3g9ujEyO6b5yzms{u|kIpj)T?cW);+MGV>=aFTo7XyNW*_S!uG~uV)JrwMaeL zDgYl^mr78EZ~~TXPrzOEB~N0R&vFQfmINufq0`b_2+1mF;yA~oUiO3KTUB0CL_l5T zn_M^@mJ33Ks2mYWsTE{Gke!mdiyA->6I>rwE!l!5m8M{hoC>NcC0DQ|@*luOoFCg)IxQ@&K2*s$z)Jz`SQDk?R5LjcSCWl|}^E>`c zK8jK)q#O<*u117riGf5e7rrHvLI}IUI;`;ikdz9UDN^1usX>V}@2T1LvTUd3_4bYoj%uTk$jD;Yq z?x7s(R$OunJY{CUaY8UuD@^l(AsESu^V701tCckKy>+IodO`&05L|@l104tM>bBs? zyZOtS7(*+OW<0)p`i~n|0wQr-^DeZfU3(-Hp``{uSa8|Dl-da4vJ_A(RuigknOp7I zV-Zow$Xq#VfXCXxxL{5Jr@;tNQe1Hs6fI!atO@I&37IH&*d?=$3A+!mUOOc)T_D!Y zDvXhPE$OFDgL+~4p6u1E25cg33J}*jt;i>@kucMu^5%AzOh52`r-jQEwMtsyGbgwZ8{ zXCsE00qcN1L&D5*{F+q3EI%hEKm!YPJR+P;D2Z%Q8u(tN-=DsbAf1G4G zU1Gf7Vuf9Eoiw1yLPuT4Sib!5F>>T=yZJSs-CiPGI`=CUG5i@yOQXRo^&dte+Q`jZ zxu*LY=dA{WS1T01<69(&LQD1^S+i|Gf*f#4iE)k z0SYT&=oWKReW@XsMd_`iF<_&^(S;_hNn1QKhs3(CH>-8J0_P&7jrJ5E#Mt8`ts(j{H7Jp?(2ws~=|BJb*O4Qv zcaWrxD}{Bklp+lqm@tIS7X%Sed59s_F)Qj~7HSyH$*ek2VC`!>`ajWB;Jqk&y-b~E#VXV3( zqwF^X6hwGOm0TaMTBaGa#$G84scP~E0snowY<7~tIVL-3=ef8{wPXev$Fh#$I(bB; zy%20qU?5!0^5w(#kIyecNdNYAi?{)PGsdOJ1n)SlvV_E>>}k}j3dA)2~xG zY?o{U0k$yBglNbPXiaU)SVc1NX-m>N!IGdITVxl$mG4CWYzgAXa^^Z2ibAQj{aEdf zU0IY`DYua0_uMlKixq*I_;12vS=pRy?%e{eboa7gJXAL9{6LfB5LB`MBeR#*Zgs-! z!OeUW@R`fOPS4IXATYES1PlSRn+;qd>FB5BOzS_uSz*E--rqmb_W-644*{k?Q%1P5 z1ungT8l*H541^5(XfBA@pLUbp0w1{<-4qCbC3b01sYmu-;7W##9g0sSvCcX<&01_H zC4CE(S-(1sf!BDD3~7e_R8)n01=4(|I$gL3HD;?5gg>O}unA4JEy)$eq0cS{OZ}Ro z0^G!3yx+cYhsP(@Be!2A-ITdf!;%4;xvOx7mhgXI>xm8!TFNnE$E zZ2yNH4*N`0@%N!Gk!VQI#ooZ5tx&%5^QL+7nJiq)S<%gcsemnMgL|z`BQhux0!`Q+iufae-&HZ`SO0 zunArk;3h#Q>wsaBZfz@cici|gYQkJ@?pn;Mlx$})D8^CBrUz!1pfyaQ3@diQo<%+@ z3Bf6wi08m^?8hW{iEfsiRvZfoVdN|ibhi|Stc{d_RqsALJU_g9yMO;v5<)_??!rXv z*9b0XskBmuIP|e)5-b}xb2)3!5+QfX%1IIKk}kryj3flFnNcpRJ2ER7IaOE}z?2H|k_8V75b2oYt|fj8OwfS#~uZ2%k#2-EBy zu#E_c>^T|zHHpMD8(B$kJjIc;9(wJw6(4!G6`Ab8 zKR&-bTz+%^@$n&9;}L$|g$LMUS2oLKN1vg8sD!2dBALJCD`j=cc+{po(de?eS~^iS z^N{gD2*rdK+S}|kJu(l1g;fDnq!rnaiuFFd*EyLG=3={f=bp7O8FW?q75jK(`#>#j z^>NEYR`Vv64f>JAp<)4aLH~1jfn|v1>>6FyN!?yt1>P%uQp~4OVebGOHP(Ml=BaAMzOCTx`zA``4GZ8!NPJcM%sv zxYavD6Ly=>0n4nPK7TXv&43t45syGnh=H zed<`XS-4syqykv8o1__b(Ut{%>F|W+)kh+JA#FEwtH=*9z7XJkk|O8Ri~(hKHjGsGCW~AirR_ade!QA9Ec$#8 z32J85PI@t>qjq7%fD(WNq$Y<_7UB@FmLb*3k{na23Qf2QV4ooQaF#~8BMBuER&Vqq z>Y3@kdbUG*o3c10g{oqzr2UJ>=k5C!c%#4hIzq;NM`pB#wvBz-n}SIz7NuoJ;S_e~ zmUveR(jpx|NFYD9?T4L(F%OZ0=uLu*-862Bg=gf?DHYj5EOoz4Fi?uQ2<~auYgWXw zX&DM?%^@0+n5Fk)xRez8Xe5CMQWDx)uc`??D(E1{;kE&9xs8EVhJMHFzGmIBFBZ&4 z288|OMq#IzfvCbZ2`MX!Wm1}LP=pM#bif^W8weagnY$RNv353^_1S{dg<6d_BmEX@ zmFQAy+66EJX|=aq`^Ejozl5z!z1x`oL*ejxhGs`VVQ&Y(C^Rp1cX_?gWiaS4)=;j>*DBR;eDDejQV<+*Y7%Eq$fR<17<9^eVN!>JXWb z-bHB_`>8odL^fF)GZEakZSLpKpXXji%1wE>pI2U#@)*U`J&Wn_NJl2pm6>tX8fMKH zcb#FpOX)nW>|rpWcYpz~o?TwVh&g~Ey$xF?q~oe&C+<4s-d(9u*RiG+Cg|;{4a;&$ z4rG0sy!KrNU7x+`R?{UC?berzV+mxGv`ZG&_MR$I0YPQk5GExX^t6{+E40n#*dlZ2 zZ;^*&E_*m8pI?J7*1RoMLM(N&>PLzce#mAHNaE!jEs-IGE!FmqyU?=OPs`T`r2PSG z_o|SueX=~)_123lg+A1e4ZZxY7wGtsWE(y)#Oiv4k!QrknCz>pgZRK~s*^58nOH~L z+>=c*YLY!f4J^e-<~?!B!@x$CiFgvJC=WIbX zhDxxOTI;w3fK{UHi6L_j>>l9Q5bNB8!k9{73N7Qu+IZ4J4q;{Wzms&_eJKb-SJiJPVh>*n2O3Inp*g_BiTW7{V zZMxsp%B(P;T%NHdYumm8r!B$JX@X&pNvp_tvbJ>{8VM8OT{yYH7EAQ|6y`x{4yS8v z+1Ad?xSm~E6~}E7lej%pL}Op$7zbyD31w5?e|r7yr`cmklT}FQl~FWw3i9AILCzvw@N^g#n|=e{q2s_w z8_CAxRc~~004RqHIk&Jn5@~bj3H}xsiri*6C$LFT8v;Z`*VX>+`4+~BkcgUdF^QDy zGNt@7Z-H6nWw$va4=Vh{0Hqr3Tz>iZ@a*ib0b^LQNr1;G+yB9&h`Kw<)x<;8Pdl~Vk{o9!Rnsop3-+XZOuV_=LW}Buu6RaG zzp2%&lPm2}kw|94t8GcLy#%s_()QfoUp#z#zW@0C@p?*W6R(xSANEV+yAIEzJYJBlWmspX_e@{h$rYfH|TfRPaJSZ{4I zW%4JgT(H$PHi$1)5e%BC=#h=tpx5>NcnKvG0UFzbp<-8x_KceFW$4{?!yU1HlQLln!6tdkm}l3 zW|KCWxFs|=s_S%&&t`v9-P2DpIHVb`9+Ube1fLWoLGW#f7n@v?W_A2%i5QzuC2?%i zrm2(JwtMEZglb79hkr6Oy7KzSMco}y>37A#fs`bR1FWr%P&g;PVE#%>PVe+DUA(@tTF_92hOw#+4l?#syudfFRQbo zJInM zp=jOFRy`>Yn|{r4e{eP_?b^heN#Ou%LH3;J-3piZUN{3;-pEPe6%*9Y@_v zP_Ey&ra8je-bmEW?)>D`G9P-~7BE2@y>CRpk#iFnY9pa~%_{zWTqe#u98;7XZ!qfR zzEcmiCl7HsJ*#7rGH1WDQ_!<84Ja%Nf{30U2qsU%!UHoW9b2aah1-^=3vi(HVHn~g zqXGT-o~6T{oeCrA1tHFhT4UKSyu2IBxho8`i=-Kb@w#3^VIygD;JEWOLG=KToem4< zPJ;Y)ru8p15pF_GNq^)w{@$?CdF>9yK1;eM8vs;UvS#N{fS)=}h3#yo({&`(n+eI+ zKKn9Ew<-SJ=0sWKsabzGW=Xmv&Nn8sX${6fa$4V1J(w)SpVmdZ`F^9^*;!$w9f}A| z0#4s5v+a-gr|0gO5z*azwyd!3cH*@%i>t+fAlu{;9H|2%!)RkncE{R{lx90O#5@(q zoD8bYQ2R0HF|WYFK!Qp@yU&{Q;e5^}$m>&_bBGMdg}5&bUGr?1-~E9D0)Uo7r>-I7 zP}Q8V;lzk47+SNd$1*4&eU__^+3AX=V=STS=g4t~HSNn}7c$~xm4+d0Q&@1bbd!x^ zi66)bDb^d0Tf2hx!+i}|Uc?{B)*0w^v9T^QHukn*4L=k4Od$n9WBtmx{bKSBKRh-|Jsr(7AQLmH%E>=gp#(_d+z^jza zmfTIp!o!(rV%8&1vN@+;FIcW&T*0I%f)bgJAc)crOk1r6%OG}8Q1wll16z)qmtj&1 z>tL}2-s~Z4vlt4Mc#*nt`ThN;rwGk{ix}*85oE4cD$#At;f}Td%?`Ob$tpSMJZZFu zg>7new|_{0s7jW!B%cjZr)15Uf!GCW^SRw0CY#%lRT5JN3b)v7Cf1Csf*TJ2p)P7S zRxJk$R4RHU`EgLe@h{dH*NKeJkx<8MJ`LoSkIa$Yk}z-0I-~%!htqDjCj7BwWPaM019(pb#I_NH%YK2gj})>Zk3y`I2q4FMv{%xbIMI(>q17bygxS=)5vMtF^!aX z1O{}YoK6>BXVbII#mSNjo!F9APw9+AT31-@kZ5w|PiUg6BaOSG2PKbb*AHXqHaC}w z;SV2jcOwqF3roSMs_^wh)0ZUo>`6VAt1vu^z-;maW$NM#ij4_^#gd$|mJ_s`s8cSV zj{}k!CV#Z#2?(3KzEtWwC8)~O06gPvbX6QOzaRi_wnx(limOS^ah3mJ6=*mCvO6le zOdKP@ANh?Z*-;a$?vt%XtEgv_ZP_*hpznBeV~0EfL>mUk4Xk+Xo&q$Ia^j;qxE^7o z*tI&}{WP}IxB0vvb=ys~TaLK?jt7{_B4KD(4#XW9!$!AY8C)zz0BM%j(6kf4k#(OQOL!kzm}= zILSdJXW%7JUc-qm2|LIY1Nt}L$QB9|uGKk2lD4euxf@bPtyA?012L9}0zDOcP)3i< z5OEk5X;>_tkEjfE3UlN9^D6Iq^_8(WxDe?qpXUHstI62T4cp3`{+-hz$O`R5r1!Zj zPUlU3!Q4$}d#_H&&AiJN-7{Ckc(T}X$%$$<7h%8Z!onWTq0_NA`fZ4e>}#T$`X-WT z!*%$Jgc24BV82DsvjD8@u1&qkrQpM)}iM;j6cL7fC_AM$Q%M zYGdIxmqXOsW4Edh8VFgV>MDV5+vYG~p`o^_c8=S`K&|m+IpKoJsRZO?MoK9%*%6pN zk``5*5M~oczd1!&5K_|$Nrp#;h`!t0hoOUsTT_t@E5(j-__`|-!&DuVe zdC=DBNG$5tbv9rK{P|dv8_tB0;gjdkE3oFe8n#(%qrFXXx2K#O)}@hL2bBq5i+!v@ z2H8MWg&~AXnW!e~aMy5Q#0)Qtzb-jAHm5P2^PiT7jU=UO%fT$B&toNNCRiVgK?%4Gkn?S{Dsv!5VEPy zdH%rPCBVaaB3SDQK%VFsp28?N=Ln2B`9X(e(a64kM}Mwa%g$g=ip=W3nT0(!CKG1J za}6#zS)J7skz8}+M-GDq>t##JVj3)SBoDgW zXLD{wqfUT7d%hDjiCmA&miw~b&QcMZqRZNGWLYWHmC99^2bMv~OhNehr*A*KzCAwY zxL3G1cfD|GVDY3g&s7k#x4(#{P-n#e6*l`Clhxg8lG%Dr+?>ID^A1NR z)?_o(BWGJUR&b)$9*&vxX(Ps#e66NYDvl%D^KePW<~htE=QK*jv{%6*Z0mEIWI@#I zQ#g~qx!T4bk6D*`=21ka-0~b-P7ijmuBb{SPTRclXW+-;-^f#MIE7$ItQhVIF-YJt1pG3nVbW*g&xieyB94*&veWQ=KKzGDp!13hW6Y@k3i3He|=tq*IIE zP+B+v_yy{+vamY{La3Q{vEk4ba_R^)q|OoGIg^^m#KCDZ)uSbCcfdHpK8#-dz3Cp%DCtCD@}x;f+u7#+(#?gpTezr+D?v(g9hc#PSxX$wXU8aQ%% zKjd|Y*dq1jsZMn3@`+ItfXhS*?L0=@bIe7}IWz)!(BOz0ciTmX6?CHA5iWL0d5E3! z66XmqK<|xC>ic&bcI>Esnf3bsr{BhvcF~O!K&7=c`8-DwORE#M&f8c+y9o9?b7GY{ zOx&L|@xv2!{dm}S-Z`U9GwO@;|Cp&^j+}$K<}_M-o%D)4r&G!n9JJ2)2tv^|@t;Ye zC=)Xt*$z7*BDw%;vjOkfEl>ckDm}A@> zs+<)div$+W8)P{7%LIwTerqXk*4U~IkQRyUaH|p)?Uke&KDJpOO!9>F2=da?;}na< zuaPw3EMQn|@$R0FS)`#j4(||mOp1P<_C-CB$q#iGi{5YQDT{bGM$L}oa-+jC6QYrR z8k*%-OI`l(>Fw#BsYwKx5g`$A=1sUbw@HAjtpOT zQ)ZQ(>9561_vGo^;wulNk^PM^RjUQ@wvkgSnun}y_9l&eapFRkHcqsNH8BzI>7(p& zgJn3+PcVH&llPil+B@$=DUyzo8Ozw!Gh-3}mM-({%%%`qN9`$S`y6_{ITdUye|UNQ z@!{zyJ3ZRAdhSiThN+B1Jg4`yTj?^Z8nUNs#cbMeY@LM;PoUYefh$Kz?KKhcUd956 z7uG8&k|sBah<>w=9Ft%uijmF}HIvL-GRMe%K1O|MUx*@Y$ACOS zeq^fRo(H(tcXwoY-{WllM$56c!7>ZUK@a(trc8pKM@oyE%Z0Yn(tgy0nM=mqQo^T3 zm@5hPC-1iBfx3CV)Mpk;f)e{=d}@dl5ux#;&2a~g87kQWo~_FvE$_bj>GGSGPmC$% zT=($Ocac=A*NyJFRJS~?8k>eklV(JnqPGmD>bl+9C#^-?2rn& zbUOQ_7dl(o6D-+pr&t)964U4KoIU5n zVrMUC)$!w1YV?T<_Rj7JPX1BZrn5vo9xMRwG>JO;VJ3yL9W!&T4-Hy6%GNr zp_-;B<&NE9y#dd$WE`U*O68jRi=0VLXFv`2HvmM7dxfs z)i0V>+qkgUi43$1=JT9$@9H2?hwW&noV8?ogvG(+eABVcsjjNOq?co4$qIDU0bXlz zVdPSR4E9-<)mDvcUpSR|!dGblm0V*cbMtTwP=DC59;ww7eynK8=&sMMJT_nTn6?vq4 z-zgTkl&2N)+%_w$HvoMVYg&_Y4~u4NGeBGI?cp%b+u-y(mc?-&hA?sfM;_^9l_N*9 ztR}PB;btL)uYyH!gw^U<^t;>~p+<;>z$P+G<@y5-+a+G@(AGZNGw8?@I{ZUni4e{_ zToVhqdoZPKwuhiRb->|(LUGGB9i^LcoC~Zn$@VQvA6uC~Q=XXVNewysS>!*DB(Gvg zmaw7N<{WA)mGExObKX)Jx)ULLj%ZuwWLqtbmHbQ1iL9c^maHW|Hs8aepFLdYn4>eC zyekK(+1_AXP#r$dRlg=JJ1f3U zFw1#jG`UL5STncFvymP4U$f%A=z$%LtsP(UScrYHp-mP%irp`H8cfss^gP=v+3`I+ z$i7hjwpN`ZQ%h#K$Cz{F)}+9kY4~%1T+3s1j!coW$0WSBY_HqQsL#i^Q9iI};1L76 zZE>EbFr3{yr{^U8$0<>5GM8JP8f|4-r{<-t*4yu=YZEJ3jYFMWM%-+Vz>e<3Sc`5-&nwTXm%OfhAxv)taZz#^dfgbzvX#8D2>RDBUS2{3BZ}7 z;#+o3c;355qu@EuL@gX#ea>^splVT~l08@*7nAKcP7670Fw)u0CIBD1X0N_1S@|@F zf%xLMQ?m~$bbaKT)7>SlkA^x&C$>D89bCfhJ85DT=fJi^J&DMqzd2!Uo#BlV;!uRc z8Ef0*8|C~vjLygt1sYR|v)i;y;KJCOP2z8cwKgBnRysA+M7AX- zfo8~4VYz>P`R*ad{6}hWcZOSK>~$5N!_{sOE^C&-?b@e9@^G?T&C1pBd9?aj=D@Kn zN0O|Zf!;m#zyV`6b3pQx^(xaniBiC6PW=Ya@c5ZBvOBOkdczL*6I>1p*IS+(O0Mde zwd9+QWcA=lTGO{1ob8;C9CzXb)P(3}9E+PgEH`iRAgCcK`dAA5ac;f1o6N;;+ZLwtCW7Iw~TRb@$iL9wtw&Ub;z?F zSGVN7uSwJO)LA^8vl5(&>hVH(zLy$&${pLXmWinrR*)h%=Ey*YGGm<8J`X;_-?DPB zz6p9xP_~g1h`=w*O^YL}dq()XNW0IT`DoUraZ0`B>F9JFEe?Z5W*i!**>P-(6#P}# zWS7{NGm6nsD}ykp?fd}KW2-$0+98)ntXwwRP({ZlQ@rKLxv3cTAm`x?>28aoA`s1* zJWZSLji;Mu&aD4QY&^QLx4Rx%5~(tS-&oDfQ({-&#;I>`pO3uvkw)9DtC1s}z$<}Ai@Z)7Q_e{o^YomdCH4@MGx-%f>}Eg4)MJmm4YQKt0eF?1 z(k2+>IUh1TYNp+Ub9S6rx$%H7nr6a{XWVvn5Q_^!O z3~xGmJ7BpcCs3_4S130qP#{ePDUr0JFAc%POxH6{%F5H(tdg%5P$k2x6EL@r?hrRN z>doD+d9vBsZ!JU)4U+<>5ve(Oq$k8^t59ZZwo;giOE7dOH?gfO6iEZtA$`@Bc~_ae zFvUi^W6$wKIRx9ne{B07K0H1@Py-++=4i3~*dn*y>hgP+@^ z63M;JaeF19jya@bH9EP7{lVHe3_gc+uk0+YHMw3@$U&OPcH@W085MJ=lC{=8CzRH# zr<}7l!@(FePev~}!cB7pnk#3>bd4!523pxxpHsx}t42Od2DXW|Tj%p6#_d+x_kes?PS8ecVzpr(}XGlt=OidvRGJkW{#+dqXj+np@b3-K}lxYNIPM~v*-BwgDd zYNyMdZYMeRc}}}5ZNVc_>FP<3!~^F+2}rQZapAT^$--q6w&nOTaj#|v3E7zQ@Ep$( zFPx`wYdBZW_{Apd4vwEdeBEaAQ!=?vD^J23-I*1;EpM`%YRfiS`WKCLw1(DyoQ>(Z zvB(UD!($HTuyCs+J-dj~PsK+iDPag5K66xy6 zWZPyMIiPlDhq?@$eICYO{NNb0WQaRTUoo^zx2I9h$-Exll3paz45?E+S75uH{9^G- z^J}DOMX#mnF|Ezfq_)aROLIC7&|Nd0&VwF3R!r(7h>FHeu+CW*vMF<1Mo8MXwk3V} zZ(oP0;rzOdBZ&?QtBdwF2VmKrt;(j>=aww@5)nH_yO9^3=mXi%!*bUgL#g-$%KV-QEI%q z3|%2_hn?&gxI8ShXY+2g>9Qx$>`YwDL~n=(h>?AA-c@4@ZHOiLlUK^z`HnS)wddadVAY4tpUi3U$beg zIciu^pTvoCi(pIwv)SY>3FbDJ+%D2GM0B<(aq=OK957>Wj#IsV?^&ImE!^5&XjWtP z1&gTZP{fX$RWMKPAfX~?YBGjS5!pgn@ z=zHxX z+WlW2p7EcKu`0)1?8_#3^jumd8f7B(JWlqGC{l;yu1$Gwvir7uBCorRF%T0d#(5dL9(a|%%h%!!&As%@hIjpDb#h9oN@?a<-zQk2AltwCP*b;$IR88 z+>DbzOC4}hTMApANotFKcZYBYo8ZYI&pa(ra%x0c_9e&X!!6KRNpFCdN{DP|IGg@< z+pczL26oyzTXtLi@$%2l?=LUkT)ugHdbs?<_wn!R&)(m^{loHq0Bh)0JJ_QD0BGF2 A*#H0l literal 0 HcmV?d00001 diff --git a/share/doc/networkx-1.11/examples/drawing/labels_and_colors.py b/share/doc/networkx-1.11/examples/drawing/labels_and_colors.py new file mode 100644 index 000000000..cf06a95a7 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/labels_and_colors.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +""" +Draw a graph with matplotlib, color by degree. + +You must have matplotlib for this to work. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) +import matplotlib.pyplot as plt + +import networkx as nx + +G=nx.cubical_graph() +pos=nx.spring_layout(G) # positions for all nodes + +# nodes +nx.draw_networkx_nodes(G,pos, + nodelist=[0,1,2,3], + node_color='r', + node_size=500, + alpha=0.8) +nx.draw_networkx_nodes(G,pos, + nodelist=[4,5,6,7], + node_color='b', + node_size=500, + alpha=0.8) + +# edges +nx.draw_networkx_edges(G,pos,width=1.0,alpha=0.5) +nx.draw_networkx_edges(G,pos, + edgelist=[(0,1),(1,2),(2,3),(3,0)], + width=8,alpha=0.5,edge_color='r') +nx.draw_networkx_edges(G,pos, + edgelist=[(4,5),(5,6),(6,7),(7,4)], + width=8,alpha=0.5,edge_color='b') + + +# some math labels +labels={} +labels[0]=r'$a$' +labels[1]=r'$b$' +labels[2]=r'$c$' +labels[3]=r'$d$' +labels[4]=r'$\alpha$' +labels[5]=r'$\beta$' +labels[6]=r'$\gamma$' +labels[7]=r'$\delta$' +nx.draw_networkx_labels(G,pos,labels,font_size=16) + +plt.axis('off') +plt.savefig("labels_and_colors.png") # save as png +plt.show() # display diff --git a/share/doc/networkx-1.11/examples/drawing/lanl_routes.edgelist b/share/doc/networkx-1.11/examples/drawing/lanl_routes.edgelist new file mode 100644 index 000000000..db831813c --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/lanl_routes.edgelist @@ -0,0 +1,1363 @@ +1 0 9 +2 3 173 +3 4 167 +4 5 165 +5 102 96 +5 6 96.43 +6 7 96.62 +7 8 86 +8 9 87 +9 10 82 +10 11 79 +11 12 74 +12 13 41 +13 1 3 +14 15 187.46 +15 16 187 +16 17 187.04 +17 18 186.62 +18 19 185.88 +19 20 185.4 +20 21 184.02 +21 22 184.38 +22 23 183.97 +23 24 93.01 +24 25 88.59 +25 26 86.26 +26 27 88.04 +27 28 77.12 +28 29 74 +29 119 70.1 +29 30 72 +30 31 73 +31 32 89.41 +32 13 6.63 +33 34 212.25 +34 35 211.46 +35 36 209.04 +36 37 211.57 +37 38 206.57 +38 39 105.04 +39 40 77.21 +40 41 86.5 +41 42 0 +42 1 16 +43 44 112.67 +44 45 111.94 +45 46 111.77 +46 47 111.83 +47 48 111.27 +48 49 111.76 +49 50 109.32 +50 51 110.67 +51 52 80.63 +52 53 80.78 +53 41 30 +54 55 164.62 +55 56 164.13 +56 57 164.24 +57 58 156.43 +58 59 88.92 +59 60 88.6 +60 61 86.53 +61 62 85.96 +62 10 86.8 +63 64 221.31 +64 65 220.88 +65 66 220.84 +66 67 220.27 +67 68 215.19 +68 69 209 +69 70 213.81 +70 71 208.04 +71 72 196.45 +72 73 197.05 +73 74 163.85 +74 75 186.4 +75 76 180.35 +76 77 103.92 +77 78 98.6 +78 79 98.83 +79 80 96.09 +80 10 309 +81 82 174.3 +82 83 173.57 +83 84 173.9 +84 85 173.66 +85 86 173.75 +86 87 92.62 +87 88 73.93 +88 32 91.44 +89 90 158 +90 91 155 +91 92 158 +91 97 157.76 +92 93 86 +93 94 325.32 +94 32 5.53000000000003 +95 96 158.17 +96 91 157.6 +97 98 84.74 +98 94 84.87 +99 100 282 +100 101 279 +101 5 167 +102 7 87 +103 104 201.16 +104 105 200.5 +105 106 200.5 +106 39 121.02 +107 108 148.27 +108 109 147.59 +109 110 147.87 +110 111 147.88 +111 112 147.82 +112 113 140.19 +113 114 147.54 +114 115 64.02 +115 116 67.75 +116 117 70.55 +117 118 98.3 +118 119 80.4 +119 31 81.28 +120 121 161.26 +121 122 160.84 +122 123 160.86 +123 58 160.84 +124 125 238.34 +125 126 237.79 +126 127 237.08 +127 128 234.34 +128 129 236.72 +129 130 237.18 +130 131 119.25 +131 132 111.15 +132 133 30.93 +133 134 50 +134 135 49 +135 42 43 +136 137 148.68 +137 138 147.78 +138 139 147.74 +139 140 148.29 +140 141 136.56 +141 142 145.37 +142 143 144.93 +143 144 141.78 +143 160 144.92 +144 145 141.65 +145 146 72.32 +146 147 68.81 +147 148 73.15 +148 149 93.13 +149 150 82.38 +150 151 86.03 +151 152 73.3 +152 153 80.2 +153 154 83.08 +154 32 0 +155 156 152.24 +156 157 151.47 +157 158 150.17 +158 159 151.59 +159 140 145.38 +160 145 143.86 +161 162 197.3 +162 163 196.83 +163 164 196.73 +164 165 196.8 +165 166 131.22 +165 383 128.8 +166 167 36 +167 168 44.08 +168 42 11.49 +169 170 178 +170 171 174 +171 172 166 +172 5 166 +173 174 213.7 +174 175 213.01 +175 176 213.03 +176 177 213.38 +177 178 202.81 +178 179 204.73 +179 180 203 +180 38 202.46 +181 182 44 +182 183 41 +183 184 43 +184 185 44 +185 186 42 +186 187 33 +187 188 32 +188 189 39.44 +189 42 36.28 +190 191 258.85 +191 192 257.61 +192 193 258.1 +193 194 87.26 +194 195 88.26 +195 196 85.82 +196 197 18.93 +197 198 32 +198 188 33.68 +199 200 114 +200 201 110 +201 202 114 +202 203 112 +203 204 29 +204 205 36 +205 206 37 +206 42 27 +207 208 562.86 +208 209 561.89 +209 210 561.94 +210 211 9.50999999999999 +211 212 14.12 +212 213 6.81000000000006 +213 214 1.72000000000003 +214 215 11.83 +215 117 85.94 +216 217 114.12 +217 218 112.57 +218 219 112.04 +219 220 112.01 +220 188 88.74 +221 222 211 +222 223 208 +223 224 211 +224 225 103 +225 226 104.75 +226 227 103 +226 687 105 +227 98 73 +228 229 67.83 +229 230 66.68 +230 231 67.39 +231 232 67.36 +232 233 66.39 +233 234 32.11 +234 188 32.19 +235 236 194 +236 237 191 +237 172 166 +238 239 191.94 +239 240 190.85 +240 241 191.48 +241 242 190.73 +242 243 183.14 +243 244 182.32 +244 245 179.92 +245 246 182.17 +246 97 173.79 +247 248 195.23 +248 249 194.78 +249 250 179.06 +250 251 179.01 +251 252 168.31 +252 57 165.26 +253 254 92.14 +254 255 91.07 +255 256 90.41 +256 257 90.85 +257 258 46.52 +258 259 57.31 +259 260 4.04000000000001 +260 261 19.22 +261 262 8.82000000000001 +262 263 79 +263 41 78 +264 265 102.69 +265 266 102.24 +266 267 101.92 +267 1355 116 +267 268 101.64 +268 269 0 +269 270 80.77 +270 271 89.27 +271 272 115 +272 273 116 +273 274 115 +274 133 50 +275 276 58.9 +276 277 58.19 +277 278 0 +278 279 58.13 +279 280 58.21 +280 281 58.04 +281 282 58.12 +282 283 31.7 +283 284 26.07 +284 285 38.42 +284 574 32.12 +285 286 196 +286 42 127 +287 288 186.74 +288 289 185.88 +289 290 183.24 +290 291 184.06 +291 292 181.22 +292 293 0 +293 294 171.29 +294 295 102.04 +295 296 68.99 +296 30 68.93 +297 298 103.31 +298 299 102.8 +299 300 102.75 +300 301 102.77 +301 302 102.55 +302 270 101.64 +303 304 555.45 +305 306 76.2 +306 307 75.65 +307 308 75.66 +308 309 75.9 +309 310 75.7 +310 311 69.12 +311 312 68.99 +312 32 0 +313 314 170 +314 315 167 +315 316 169 +316 317 169 +317 318 169 +318 319 167 +319 320 167 +320 321 27 +321 198 16 +322 323 178 +323 324 175 +324 325 177 +325 326 125 +326 327 117 +327 328 34 +328 329 34 +329 330 34 +330 331 0 +331 198 33 +332 333 85.6 +333 334 84.88 +334 335 84.69 +335 336 77.26 +336 337 80.75 +337 53 79.65 +338 339 104.12 +339 340 103.35 +340 341 103.55 +341 342 61.24 +342 343 90.09 +343 344 92.58 +344 345 92.92 +345 346 86 +346 9 86 +347 348 106.96 +348 349 106.37 +349 350 75.33 +350 351 90.59 +351 61 66 +352 353 76.11 +353 354 75.34 +354 355 75.16 +355 356 74.84 +356 357 61.66 +357 358 103.06 +358 359 34 +359 168 34 +360 361 122.16 +361 362 121.69 +362 166 121.31 +363 364 63.84 +364 365 63.35 +365 366 63.5 +366 367 48.79 +367 368 47.81 +368 369 38.89 +369 370 38.77 +370 371 37.31 +371 358 36.43 +372 373 210 +373 374 206 +374 375 187 +375 376 343 +376 377 193 +377 378 33 +378 379 86 +378 866 33.57 +378 958 33 +379 285 196 +380 381 51.17 +381 382 50.66 +382 383 27.75 +383 359 32 +384 385 44.03 +385 386 43.56 +386 387 43.19 +387 358 40.03 +388 389 79.01 +389 390 0 +390 358 78.04 +391 392 186.71 +392 393 185.92 +393 394 184.8 +394 395 171.14 +395 396 176.31 +396 53 82 +397 398 133.54 +398 399 133.05 +399 400 133.09 +400 383 132.6 +401 402 101.49 +402 403 101 +403 358 100.55 +404 405 199.34 +405 406 198.85 +406 407 198.94 +407 408 199.01 +408 409 198.79 +409 410 53.72 +410 411 33.45 +411 412 38.86 +412 413 36.46 +413 414 38.68 +414 205 31.92 +415 416 35.64 +416 417 34.93 +417 418 34.91 +418 414 34.93 +419 420 119.9 +420 421 119.37 +421 422 119.02 +422 423 118.89 +423 215 111.28 +424 425 57.09 +425 426 56.3 +426 427 54.33 +427 428 55.68 +428 429 55.89 +429 430 52.57 +430 283 39.54 +431 432 91.65 +432 433 90.74 +433 434 91.05 +434 435 90.27 +435 436 87.89 +436 437 83.17 +437 438 76.52 +438 439 67.26 +440 441 32.05 +441 42 32.57 +442 443 103.73 +443 444 100.75 +444 357 103.22 +445 446 226.87 +446 447 226.41 +447 448 226.52 +448 166 225.66 +449 450 107.54 +450 451 107.08 +451 383 106.74 +452 453 190.21 +453 454 186.79 +454 455 188.77 +455 456 188.36 +456 457 41.12 +457 458 39.38 +458 459 39.48 +459 460 37.96 +460 286 32 +461 462 32.81 +462 359 32.31 +463 464 106.75 +464 465 106.27 +465 358 105.93 +466 467 34.12 +467 468 15.78 +468 469 33.79 +469 470 33.05 +470 471 53.35 +471 188 17.55 +472 473 90.41 +473 474 89.07 +474 475 90.06 +475 476 89.2 +476 477 88.42 +477 478 85.87 +478 10 0 +479 480 142.27 +480 481 8.90000000000001 +481 482 13.62 +482 483 112.18 +483 29 138.4 +484 485 101.64 +485 486 100.96 +486 487 101.12 +487 488 100.77 +488 489 49.98 +489 117 48.89 +490 491 42.63 +491 492 42.02 +492 493 41.89 +493 494 41.72 +494 460 40.09 +495 496 184.89 +496 497 184.2 +497 498 184.58 +498 499 184.51 +499 500 43.65 +500 501 43.27 +501 502 39.97 +502 286 39.64 +503 504 87.59 +504 505 86.98 +505 506 68.34 +506 507 86.09 +507 79 85.85 +508 509 35.46 +509 510 34.98 +510 511 35.09 +511 512 35.06 +512 203 32.71 +513 514 237.49 +514 515 236.63 +515 516 235.99 +516 517 235.45 +517 119 232.59 +518 519 108.9 +519 520 108.33 +520 521 108.34 +521 522 108.32 +522 523 89.23 +523 524 76.97 +524 525 76.55 +525 32 74.26 +526 527 94.58 +527 528 93.93 +528 529 93.74 +529 530 92.33 +530 531 91.65 +531 532 92.78 +532 533 52.01 +533 119 76 +534 535 278.82 +535 536 277.82 +536 537 277.42 +537 538 277.54 +538 215 269.96 +539 540 61.14 +540 541 59.85 +541 542 60.8 +542 543 60.65 +543 544 56.01 +544 470 33.14 +545 546 111.16 +546 547 109.47 +547 548 110.05 +548 549 108.98 +549 550 108.86 +550 551 47.69 +551 552 47.26 +552 205 55.57 +553 554 85.92 +554 555 83.29 +555 556 85.58 +556 557 85.45 +557 558 79.84 +558 559 76.12 +559 478 67.6 +560 561 315.3 +561 562 314.68 +562 563 314.69 +563 564 314.81 +564 565 291.4 +565 596 287.51 +566 567 281.41 +567 568 117.21 +567 597 134.64 +568 569 121.28 +569 570 138.98 +570 571 130.29 +571 572 85.41 +572 573 85.15 +573 283 35.07 +574 286 31.89 +575 576 107.08 +576 577 106.44 +577 578 96 +578 1347 96 +578 579 106.29 +579 580 106.33 +580 204 21 +581 582 182.8 +582 583 180.62 +583 584 181.1 +584 585 181 +585 586 176.62 +586 587 167.92 +587 588 33.47 +588 589 34.93 +589 580 33.76 +590 591 293.64 +591 592 293.08 +592 593 293.16 +594 595 292.45 +595 564 291.9 +596 566 287.81 +597 569 129.61 +598 599 108.64 +599 600 103.72 +600 203 108.12 +601 602 188.26 +602 603 186.83 +603 101 150.99 +604 605 172.09 +605 606 171.68 +606 607 171.75 +607 57 165.06 +608 609 108.26 +609 610 107.62 +610 611 107.17 +611 612 107.3 +612 613 94.52 +614 615 179.19 +615 616 178.63 +616 617 178.73 +617 618 177.65 +618 619 93.43 +619 312 87.42 +620 621 180.61 +621 622 179.95 +622 623 180.24 +623 624 180.2 +624 625 180.07 +625 626 182.28 +626 627 176.33 +627 628 99.93 +628 629 96.99 +629 630 96.68 +630 396 76.07 +631 632 156.82 +632 633 156.12 +633 634 155.87 +634 635 156 +635 636 155.69 +636 637 155.46 +637 638 169.15 +638 639 168.91 +639 640 168.78 +640 28 47.56 +641 642 199.77 +642 643 196.16 +643 644 199.4 +644 645 197.12 +645 646 174.59 +646 71 197.29 +647 648 234 +648 649 230 +649 650 233 +650 651 102 +651 652 98 +652 197 101 +653 654 187 +654 655 184 +655 101 167 +656 657 203.5 +657 658 202.75 +658 659 202.65 +659 660 202.31 +660 661 202.32 +661 662 202.19 +662 663 181.05 +663 664 93.39 +664 665 86.23 +665 148 93.14 +666 667 204.1 +667 668 203.08 +668 669 203.14 +669 617 177.3 +670 671 115.11 +671 672 114.18 +672 673 114.39 +673 674 110.33 +674 675 107.11 +676 677 262 +677 678 261.21 +678 679 256.73 +679 680 258.46 +680 681 258.25 +681 637 257.7 +682 683 234.24 +683 684 233.35 +684 685 232.7 +685 686 29.28 +686 225 103.48 +687 93 321.3 +688 689 89.06 +689 690 88.36 +691 692 88.72 +692 693 88.5 +693 285 88.12 +694 695 637.37 +695 696 636.56 +696 697 634.76 +697 698 628.88 +698 699 615.38 +699 700 102.91 +700 701 85.77 +701 702 78.1799999999999 +702 703 96.09 +703 165 52.89 +704 705 98.12 +705 706 97.48 +706 707 95.44 +707 708 97.47 +708 709 97.42 +709 167 43.83 +710 711 237.43 +711 712 236.85 +712 713 236.53 +713 714 236.33 +714 715 208.15 +715 716 207.98 +716 38 108.65 +716 717 107.34 +717 39 111.29 +718 719 87 +719 720 82 +720 721 86 +721 722 87 +722 723 86 +723 724 79 +724 725 32 +725 396 83 +726 727 196.18 +727 728 195.25 +728 729 194.91 +729 730 195.83 +730 731 110.77 +731 732 111.26 +732 733 111.4 +733 734 111.31 +734 735 111.15 +735 736 90.31 +736 737 93.77 +737 215 96.41 +738 739 456.99 +739 740 454.87 +740 741 455.13 +741 742 454.46 +742 743 422.76 +743 744 327.98 +744 687 326.23 +745 746 241.98 +746 747 241.31 +747 748 241.24 +748 749 236 +749 750 231.22 +750 751 91.12 +751 752 97.31 +752 753 103.3 +753 754 31.03 +754 198 0 +755 756 219.66 +756 757 218.95 +757 758 219.13 +758 759 219.11 +759 760 218.51 +760 761 218.5 +761 762 200.71 +762 763 199.49 +763 203 105.48 +764 765 186.62 +765 766 185.87 +766 5 127.52 +767 768 88 +768 102 85 +769 770 199.66 +770 771 199.14 +771 772 199.05 +772 773 196.95 +773 774 194.12 +774 775 194 +775 776 190.3 +776 777 190.08 +777 625 187.75 +778 779 203.98 +779 780 203.14 +780 781 202.48 +781 782 182.54 +782 783 177.2 +783 626 175.98 +784 785 230.98 +785 786 230.13 +786 787 229.17 +787 788 228.7 +788 789 222.24 +789 790 208.82 +790 791 96.82 +791 628 96.76 +792 793 107 +793 794 105 +794 795 106 +795 796 107 +796 797 106 +797 798 106 +798 799 91 +799 345 91 +800 801 343 +801 375 340 +802 803 191 +803 804 188 +804 805 190 +805 806 191 +806 807 149 +807 808 149 +808 809 146 +809 810 11 +810 811 13 +811 812 33.77 +812 42 16.29 +813 814 201.7 +814 815 200.68 +815 816 199.52 +816 817 199.61 +817 818 199.6 +818 819 45.02 +819 820 44.94 +820 821 26.66 +821 285 43.1 +822 823 156.08 +823 824 131.23 +824 825 149.04 +825 826 151.23 +826 827 142.62 +827 828 111.31 +827 856 109.19 +828 829 110.5 +829 295 110.86 +830 831 83.15 +831 832 82.49 +832 833 80.44 +833 834 80.99 +834 40 81.66 +835 836 126.83 +836 837 125.73 +837 838 125.54 +838 839 118.42 +839 840 110.85 +840 841 111.09 +841 842 81.99 +842 358 80.97 +843 844 619.19 +844 845 618.53 +845 846 106.9 +846 847 105.36 +847 848 65.2 +848 849 82.97 +849 850 103.91 +850 851 106.54 +851 829 105.35 +852 853 161.36 +853 854 160.48 +854 855 153.31 +855 826 145.92 +856 857 111.26 +857 858 111.27 +858 533 72 +859 860 146.09 +860 861 145.5 +861 827 144.82 +862 863 75.24 +863 864 74.61 +864 865 34.1 +865 378 197 +866 285 33.46 +867 868 138 +868 869 135 +869 166 137 +870 871 526.33 +871 872 524.67 +872 873 171.42 +873 874 167.51 +874 875 174.09 +875 876 98.02 +876 877 100.53 +877 878 104.75 +878 879 98.28 +879 880 103.86 +880 117 0 +881 882 406.4 +882 883 405.18 +883 884 404.32 +884 885 395.55 +885 886 38.38 +886 887 39.79 +887 574 36.72 +888 889 196.54 +889 890 195.66 +890 891 195.5 +891 892 34.95 +892 893 34.39 +893 188 35.13 +894 895 98 +895 896 95 +896 897 98 +897 383 98 +898 899 200.41 +899 900 199.7 +900 901 199.66 +901 902 199.24 +902 903 199.17 +903 165 194.72 +904 905 240.47 +905 906 239.63 +906 907 240.14 +907 908 239.85 +908 909 239.93 +909 910 239.8 +910 911 153.73 +911 912 139.02 +912 913 25.99 +913 914 25.8 +914 640 30.59 +915 916 57.63 +916 917 56.93 +917 918 56.9 +918 919 56.61 +919 920 56.6 +920 921 56.31 +921 922 52.48 +922 923 49.64 +923 924 37 +925 926 698.1 +926 927 696.29 +927 928 697.35 +928 929 696.84 +929 930 104.07 +930 931 112.53 +931 932 116.56 +932 933 113.51 +933 42 72.73 +934 935 108 +935 936 105 +936 937 108 +937 166 108 +938 939 247.1 +939 940 246.42 +940 941 245.86 +941 942 218.76 +942 943 218.87 +943 944 33.42 +944 188 31 +945 946 75 +946 947 72 +947 383 74 +948 949 103 +949 950 95 +950 951 103 +951 952 103 +952 953 98 +953 725 83 +954 955 180 +955 956 177 +956 957 179 +957 377 76 +957 865 146 +958 460 32 +959 960 91 +960 961 87 +961 962 91 +962 963 91 +963 964 90 +964 118 89 +965 966 306 +966 967 303 +967 968 305 +968 969 155 +969 970 303 +970 971 192 +971 972 112 +972 973 112 +973 974 111 +974 975 80 +975 41 79 +976 977 170.09 +977 978 56.57 +978 979 165.74 +979 980 169.19 +980 981 168.92 +981 982 169.16 +982 983 169.1 +983 984 161.78 +984 985 161.68 +985 986 164.96 +986 987 164.63 +987 988 144.05 +988 989 33.96 +989 811 33.93 +990 991 194.72 +991 992 193.9 +992 993 194.2 +993 994 165.1 +994 995 164.38 +995 985 164.86 +996 997 144.93 +997 998 144.35 +998 999 144.48 +999 1000 144.54 +1000 1001 142.77 +1001 987 144.15 +1002 1003 92 +1003 1004 16 +1004 1005 60 +1005 62 91 +1006 1007 103 +1007 1008 100 +1008 1009 103 +1009 1010 96 +1010 1011 60 +1011 80 86 +1012 1013 101 +1013 1014 97 +1014 1015 95 +1015 1016 77 +1016 60 54 +1017 1018 98 +1018 1019 95 +1019 1020 0 +1020 351 19 +1021 1022 380 +1022 1023 254 +1023 1024 370 +1024 1025 372 +1025 1026 379 +1026 80 311 +1027 1028 47 +1028 1029 40 +1029 1030 40 +1030 1031 46 +1031 924 59.42 +1032 1033 39 +1033 1034 36 +1034 1035 38 +1035 552 39 +1036 1037 56 +1037 1038 52 +1038 1039 55 +1039 923 55 +1040 1041 42 +1041 1042 39 +1042 1043 42 +1043 358 41 +1044 1045 104 +1045 1046 101 +1046 1047 103 +1047 1048 102 +1048 1049 102 +1049 858 98 +1050 1051 338 +1051 1052 335 +1052 1053 337 +1053 1054 337 +1054 1055 64 +1055 1056 178 +1056 1057 177 +1057 1058 177 +1058 1059 177 +1059 1060 64 +1060 1061 153 +1061 1062 31 +1062 460 153 +1063 1064 183 +1064 1065 180 +1065 377 33 +1066 1067 255.41 +1067 1068 254.7 +1068 1069 254.31 +1069 1070 218.96 +1070 1071 238.65 +1071 1072 238.41 +1072 1073 237.99 +1073 1074 81.72 +1074 1075 102.28 +1075 188 92.68 +1076 1077 87.02 +1077 1078 86.6 +1078 1079 86.7 +1079 1080 86.71 +1080 1081 86.51 +1081 1082 86.28 +1082 117 72.11 +1083 1084 37 +1084 865 26 +1085 1086 88.34 +1086 1087 86.73 +1087 1088 87.48 +1088 1089 87.6 +1089 1090 86.77 +1090 571 85.58 +1091 1092 82 +1092 1093 78 +1093 1094 80 +1094 1095 79 +1095 1096 78 +1096 1097 77 +1097 1098 79 +1098 262 79 +1099 1100 80.61 +1100 1101 77.63 +1101 40 71.88 +1102 1103 90.9 +1103 1104 90.1 +1104 1105 90.42 +1106 1107 329 +1107 1108 326 +1108 957 301 +1109 1110 157.39 +1110 1111 156.91 +1111 1112 156.99 +1112 1113 32.57 +1113 188 3.44 +1114 1115 33 +1115 1116 29 +1116 1117 33 +1117 1118 32 +1118 1119 32 +1119 135 32 +1120 1121 179 +1121 1122 175 +1122 1123 178 +1123 1124 0 +1124 1125 0 +1126 1127 43.71 +1127 1128 43.16 +1128 1129 41.4 +1129 1130 33.49 +1130 812 31.02 +1131 1132 87.82 +1132 1133 86.88 +1133 1134 85.49 +1134 9 77.69 +1135 1136 98 +1136 1137 102 +1137 1138 98 +1138 1139 100 +1139 1140 41 +1140 1141 95 +1141 1142 91 +1142 41 34 +1143 1144 352.6 +1144 1145 351.82 +1145 1146 351.73 +1146 1147 343.47 +1147 1148 323.5 +1148 1149 172.05 +1149 1150 171.57 +1150 1151 179.87 +1151 1152 154.08 +1152 1153 157.73 +1153 1154 170.23 +1154 42 55.24 +1155 1156 185.3 +1156 1157 184.13 +1157 1158 183.56 +1158 1159 183.14 +1159 1160 42.16 +1160 1161 40.43 +1162 1163 40.61 +1163 1164 32.05 +1164 1165 34.36 +1165 205 31.2 +1166 1167 39 +1167 1168 36 +1168 1169 0 +1169 1170 38 +1170 924 38 +1171 1172 240.93 +1172 1173 240.24 +1173 1174 238.02 +1174 1074 96.87 +1175 1176 107 +1176 1177 104 +1177 1178 104 +1178 1179 104 +1179 1180 102 +1180 1181 84 +1181 1182 84 +1182 28 74 +1183 1184 108.07 +1184 1185 107.53 +1185 1186 106.58 +1186 709 106.38 +1187 1188 119.42 +1188 1189 118.71 +1189 1190 118.15 +1190 1191 117.11 +1191 1192 115.11 +1192 1193 114.82 +1193 1194 104.91 +1194 41 103.99 +1195 1196 37.68 +1196 1197 37.15 +1197 1198 37.19 +1198 1199 33.94 +1199 414 34.17 +1200 1201 118.72 +1201 1202 113.14 +1202 1203 0 +1203 1204 78.09 +1204 1205 117.75 +1205 1206 117.48 +1206 1207 103.57 +1207 1208 116.96 +1208 1209 117.29 +1209 1210 106.13 +1210 10 113.75 +1211 1212 95.92 +1212 1213 95.3 +1213 1214 93.98 +1214 1215 93.98 +1215 1216 93.93 +1216 1217 89.21 +1217 1218 85.06 +1218 1219 65.28 +1219 1220 65.3 +1220 40 63.33 +1221 1222 33 +1222 1223 30 +1223 1224 33 +1224 187 33 +1225 1226 216.31 +1226 1227 215.75 +1227 1228 207.33 +1228 716 201.93 +1229 1230 163.93 +1230 1231 163.51 +1231 1232 163.47 +1232 1233 163.54 +1233 1234 91.76 +1234 59 88.9 +1235 1236 155.59 +1236 1237 154.73 +1237 1238 155.15 +1238 1239 155.18 +1239 1240 155.17 +1240 1241 155.12 +1241 1242 154.86 +1242 1243 75.58 +1243 1244 75.58 +1244 1245 75.6 +1245 117 70.16 +1246 1247 211.11 +1247 1248 210.29 +1248 1249 210.75 +1249 1250 210.68 +1250 1251 210.58 +1251 1252 210.37 +1252 1227 209.84 +1253 1254 86.95 +1254 1255 86.13 +1255 1256 86.29 +1256 1257 85.37 +1257 1258 77.29 +1258 1259 76.3 +1259 215 75.57 +1260 1261 117.66 +1261 1262 109.11 +1262 1263 101.86 +1263 1264 92.95 +1264 215 107.68 +1265 1266 69.35 +1266 1267 68.74 +1267 1268 67.13 +1268 1269 67.59 +1269 1270 66.29 +1270 1031 67.14 +1271 1272 95.06 +1272 1273 94.48 +1273 1274 93.89 +1274 1275 93.92 +1275 1276 76.55 +1276 1277 93.73 +1277 149 87.97 +1278 1279 67 +1279 1280 90 +1280 1281 91 +1281 1282 91 +1282 1283 90 +1283 1284 89 +1284 1285 90 +1285 1286 89 +1286 1287 85 +1287 9 85 +1288 1289 123 +1289 1290 120 +1290 1291 122 +1291 1292 115 +1292 1293 119 +1293 1294 116 +1294 1295 113 +1295 1296 116 +1296 1297 44 +1297 133 29 +1298 1299 109 +1299 1300 106 +1300 1301 103 +1301 1302 103 +1302 1303 103 +1303 1304 94 +1304 1305 95 +1305 1306 90 +1306 1307 84 +1307 1308 84 +1308 1142 83 +1309 1310 215 +1310 1311 212 +1311 1312 214 +1312 1313 212 +1313 1314 213 +1313 1331 245.24 +1314 188 37.52 +1315 1316 227 +1316 1317 224 +1317 1318 226 +1318 1319 226 +1319 944 45 +1320 1321 219.19 +1321 1322 218.4 +1322 1323 218.88 +1323 1324 218.55 +1324 1325 213.7 +1325 1314 214.5 +1326 1327 248.5 +1327 1328 247.98 +1328 1329 248.15 +1329 1330 247.15 +1330 1313 245.15 +1331 188 64.76 +1332 1333 140 +1333 1334 137 +1334 1335 139 +1335 1336 139 +1336 1337 139 +1337 1338 138 +1338 1339 70 +1339 1340 119 +1340 1341 121 +1341 1342 121 +1342 274 107 +1343 1344 97 +1344 1345 93 +1345 1346 96 +1346 577 96 +1347 580 95 +1348 1349 51 +1349 1350 48 +1350 1351 50 +1351 203 47 +1352 1353 117 +1353 1354 114 +1354 267 116 +1355 1356 116 +1356 1357 116 +1357 271 116 diff --git a/share/doc/networkx-1.11/examples/drawing/lanl_routes.py b/share/doc/networkx-1.11/examples/drawing/lanl_routes.py new file mode 100644 index 000000000..ed316229b --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/lanl_routes.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +""" +Routes to LANL from 186 sites on the Internet. + +This uses Graphviz for layout so you need PyGraphviz or PyDotPlus. + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2004-2016 +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + + +def lanl_graph(): + """ Return the lanl internet view graph from lanl.edges + """ + import networkx as nx + try: + fh = open('lanl_routes.edgelist' , 'r') + except IOError: + print("lanl.edges not found") + raise + + G = nx.Graph() + + time = {} + time[0] = 0 # assign 0 to center node + for line in fh.readlines(): + (head, tail, rtt) = line.split() + G.add_edge(int(head), int(tail)) + time[int(head)] = float(rtt) + + # get largest component and assign ping times to G0time dictionary + G0 = sorted(nx.connected_component_subgraphs(G), key = len, reverse=True)[0] + G0.rtt = {} + for n in G0: + G0.rtt[n] = time[n] + + return G0 + +if __name__ == '__main__': + import networkx as nx + import math + try: + import pygraphviz + from networkx.drawing.nx_agraph import graphviz_layout + except ImportError: + try: + import pydotplus + from networkx.drawing.nx_pydot import graphviz_layout + except ImportError: + raise ImportError("This example needs Graphviz and either " + "PyGraphviz or PyDotPlus") + + G=lanl_graph() + + print("graph has %d nodes with %d edges"\ + %(nx.number_of_nodes(G), nx.number_of_edges(G))) + print(nx.number_connected_components(G), "connected components") + + import matplotlib.pyplot as plt + plt.figure(figsize=(8, 8)) + # use graphviz to find radial layout + pos = graphviz_layout(G, prog="twopi", root=0) + # draw nodes, coloring by rtt ping time + nx.draw(G, pos, + node_color=[G.rtt[v] for v in G], + with_labels=False, + alpha=0.5, + node_size=15) + # adjust the plot limits + xmax = 1.02 * max(xx for xx,yy in pos.values()) + ymax = 1.02 * max(yy for xx,yy in pos.values()) + plt.xlim(0, xmax) + plt.ylim(0, ymax) + plt.savefig("lanl_routes.png") diff --git a/share/doc/networkx-1.11/examples/drawing/node_colormap.py b/share/doc/networkx-1.11/examples/drawing/node_colormap.py new file mode 100644 index 000000000..6a2d28d4b --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/node_colormap.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +""" +Draw a graph with matplotlib, color by degree. +You must have matplotlib for this to work. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +try: + import matplotlib.pyplot as plt +except: + raise +import networkx as nx + + +G=nx.cycle_graph(24) +pos=nx.spring_layout(G,iterations=200) +nx.draw(G,pos,node_color=range(24),node_size=800,cmap=plt.cm.Blues) +plt.savefig("node_colormap.png") # save as png +plt.show() # display diff --git a/share/doc/networkx-1.11/examples/drawing/random_geometric_graph.py b/share/doc/networkx-1.11/examples/drawing/random_geometric_graph.py new file mode 100644 index 000000000..d39fec6f3 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/random_geometric_graph.py @@ -0,0 +1,33 @@ +import networkx as nx +import matplotlib.pyplot as plt + +G=nx.random_geometric_graph(200,0.125) +# position is stored as node attribute data for random_geometric_graph +pos=nx.get_node_attributes(G,'pos') + +# find node near center (0.5,0.5) +dmin=1 +ncenter=0 +for n in pos: + x,y=pos[n] + d=(x-0.5)**2+(y-0.5)**2 + if d +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import zipfile, cStringIO +import networkx as nx +import matplotlib.pyplot as plt + +zf = zipfile.ZipFile('sampson_data.zip') # zipfile object +e1=cStringIO.StringIO(zf.read('samplike1.txt')) # read info file +e2=cStringIO.StringIO(zf.read('samplike2.txt')) # read info file +e3=cStringIO.StringIO(zf.read('samplike3.txt')) # read info file +G1=nx.read_edgelist(e1,delimiter='\t') +G2=nx.read_edgelist(e2,delimiter='\t') +G3=nx.read_edgelist(e3,delimiter='\t') +pos=nx.spring_layout(G3,iterations=100) +plt.clf() + +plt.subplot(221) +plt.title('samplike1') +nx.draw(G1,pos,node_size=50,with_labels=False) +plt.subplot(222) +plt.title('samplike2') +nx.draw(G2,pos,node_size=50,with_labels=False) +plt.subplot(223) +plt.title('samplike3') +nx.draw(G3,pos,node_size=50,with_labels=False) +plt.subplot(224) +plt.title('samplike1,2,3') +nx.draw(G3,pos,edgelist=G3.edges(),node_size=50,with_labels=False) +nx.draw_networkx_edges(G1,pos,alpha=0.25) +nx.draw_networkx_edges(G2,pos,alpha=0.25) +plt.savefig("sampson.png") # save as png +plt.show() # display diff --git a/share/doc/networkx-1.11/examples/drawing/simple_path.py b/share/doc/networkx-1.11/examples/drawing/simple_path.py new file mode 100644 index 000000000..b7f70b9fc --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/simple_path.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +""" +Draw a graph with matplotlib. +You must have matplotlib for this to work. +""" +try: + import matplotlib.pyplot as plt +except: + raise + +import networkx as nx + +G=nx.path_graph(8) +nx.draw(G) +plt.savefig("simple_path.png") # save as png +plt.show() # display diff --git a/share/doc/networkx-1.11/examples/drawing/unix_email.mbox b/share/doc/networkx-1.11/examples/drawing/unix_email.mbox new file mode 100644 index 000000000..a3a7cf8dd --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/unix_email.mbox @@ -0,0 +1,84 @@ +From alice@edu Thu Jun 16 16:12:12 2005 +From: Alice +Subject: NetworkX +Date: Thu, 16 Jun 2005 16:12:13 -0700 +To: Bob +Status: RO +Content-Length: 86 +Lines: 5 + +Bob, check out the new networkx release - you and +Carol might really like it. + +Alice + + +From bob@gov Thu Jun 16 18:13:12 2005 +Return-Path: +Subject: Re: NetworkX +From: Bob +To: Alice +Content-Type: text/plain +Date: Thu, 16 Jun 2005 18:13:12 -0700 +Status: RO +Content-Length: 26 +Lines: 4 + +Thanks for the tip. + +Bob + + +From ted@com Thu Jul 28 09:53:31 2005 +Return-Path: +Subject: Graph package in Python? +From: Ted +To: Bob +Content-Type: text/plain +Date: Thu, 28 Jul 2005 09:47:03 -0700 +Status: RO +Content-Length: 90 +Lines: 3 + +Hey Ted - I'm looking for a Python package for +graphs and networks. Do you know of any? + + +From bob@gov Thu Jul 28 09:59:31 2005 +Return-Path: +Subject: Re: Graph package in Python? +From: Bob +To: Ted +Content-Type: text/plain +Date: Thu, 28 Jul 2005 09:59:03 -0700 +Status: RO +Content-Length: 180 +Lines: 9 + + +Check out the NetworkX package - Alice sent me the tip! + +Bob + +>> bob@gov scrawled: +>> Hey Ted - I'm looking for a Python package for +>> graphs and networks. Do you know of any? + + +From ted@com Thu Jul 28 15:53:31 2005 +Return-Path: +Subject: get together for lunch to discuss Networks? +From: Ted +To: Bob , Carol , Alice +Content-Type: text/plain +Date: Thu, 28 Jul 2005 15:47:03 -0700 +Status: RO +Content-Length: 139 +Lines: 5 + +Hey everyrone! Want to meet at that restaurant on the +island in Konigsburg tonight? Bring your laptops +and we can install NetworkX. + +Ted + diff --git a/share/doc/networkx-1.11/examples/drawing/unix_email.py b/share/doc/networkx-1.11/examples/drawing/unix_email.py new file mode 100755 index 000000000..ae95b729a --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/unix_email.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +""" +Create a directed graph, allowing multiple edges and self loops, from +a unix mailbox. The nodes are email addresses with links +that point from the sender to the recievers. The edge data +is a Python email.Message object which contains all of +the email message data. + +This example shows the power of XDiGraph to hold edge data +of arbitrary Python objects (in this case a list of email messages). + +By default, load the sample unix email mailbox called "unix_email.mbox". +You can load your own mailbox by naming it on the command line, eg + +python unixemail.py /var/spool/mail/username + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2005-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import email +from email.utils import getaddresses,parseaddr +import mailbox +import sys + +# unix mailbox recipe +# see http://www.python.org/doc/current/lib/module-mailbox.html +def msgfactory(fp): + try: + return email.message_from_file(fp) + except email.Errors.MessageParseError: + # Don't return None since that will stop the mailbox iterator + return '' + + + +if __name__ == '__main__': + + import networkx as nx + try: + import matplotlib.pyplot as plt + except: + pass + + if len(sys.argv)==1: + filePath = "unix_email.mbox" + else: + filePath = sys.argv[1] + + mbox = mailbox.mbox(filePath, msgfactory) # parse unix mailbox + + G=nx.MultiDiGraph() # create empty graph + + # parse each messages and build graph + for msg in mbox: # msg is python email.Message.Message object + (source_name,source_addr) = parseaddr(msg['From']) # sender + # get all recipients + # see http://www.python.org/doc/current/lib/module-email.Utils.html + tos = msg.get_all('to', []) + ccs = msg.get_all('cc', []) + resent_tos = msg.get_all('resent-to', []) + resent_ccs = msg.get_all('resent-cc', []) + all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs) + # now add the edges for this mail message + for (target_name,target_addr) in all_recipients: + G.add_edge(source_addr,target_addr,message=msg) + + # print edges with message subject + for (u,v,d) in G.edges_iter(data=True): + print("From: %s To: %s Subject: %s"%(u,v,d['message']["Subject"])) + + + try: # draw + pos=nx.spring_layout(G,iterations=10) + nx.draw(G,pos,node_size=0,alpha=0.4,edge_color='r',font_size=16) + plt.savefig("unix_email.png") + plt.show() + except: # matplotlib not available + pass diff --git a/share/doc/networkx-1.11/examples/drawing/weighted_graph.py b/share/doc/networkx-1.11/examples/drawing/weighted_graph.py new file mode 100644 index 000000000..74e265144 --- /dev/null +++ b/share/doc/networkx-1.11/examples/drawing/weighted_graph.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +""" +An example using Graph as a weighted network. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) +try: + import matplotlib.pyplot as plt +except: + raise + +import networkx as nx + +G=nx.Graph() + +G.add_edge('a','b',weight=0.6) +G.add_edge('a','c',weight=0.2) +G.add_edge('c','d',weight=0.1) +G.add_edge('c','e',weight=0.7) +G.add_edge('c','f',weight=0.9) +G.add_edge('a','d',weight=0.3) + +elarge=[(u,v) for (u,v,d) in G.edges(data=True) if d['weight'] >0.5] +esmall=[(u,v) for (u,v,d) in G.edges(data=True) if d['weight'] <=0.5] + +pos=nx.spring_layout(G) # positions for all nodes + +# nodes +nx.draw_networkx_nodes(G,pos,node_size=700) + +# edges +nx.draw_networkx_edges(G,pos,edgelist=elarge, + width=6) +nx.draw_networkx_edges(G,pos,edgelist=esmall, + width=6,alpha=0.5,edge_color='b',style='dashed') + +# labels +nx.draw_networkx_labels(G,pos,font_size=20,font_family='sans-serif') + +plt.axis('off') +plt.savefig("weighted_graph.png") # save as png +plt.show() # display diff --git a/share/doc/networkx-1.11/examples/graph/atlas.py b/share/doc/networkx-1.11/examples/graph/atlas.py new file mode 100644 index 000000000..418ac6363 --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/atlas.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +""" +Atlas of all graphs of 6 nodes or less. + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx +from networkx.generators.atlas import * +from networkx.algorithms.isomorphism.isomorph import graph_could_be_isomorphic as isomorphic +import random + +def atlas6(): + """ Return the atlas of all connected graphs of 6 nodes or less. + Attempt to check for isomorphisms and remove. + """ + + Atlas = graph_atlas_g()[0:208] # 208 + # remove isolated nodes, only connected graphs are left + U = nx.Graph() # graph for union of all graphs in atlas + for G in Atlas: + zerodegree = [n for n in G if G.degree(n)==0] + for n in zerodegree: + G.remove_node(n) + U = nx.disjoint_union(U, G) + + # list of graphs of all connected components + C = nx.connected_component_subgraphs(U) + + UU = nx.Graph() + # do quick isomorphic-like check, not a true isomorphism checker + nlist = [] # list of nonisomorphic graphs + for G in C: + # check against all nonisomorphic graphs so far + if not iso(G, nlist): + nlist.append(G) + UU = nx.disjoint_union(UU, G) # union the nonisomorphic graphs + return UU + +def iso(G1, glist): + """Quick and dirty nonisomorphism checker used to check isomorphisms.""" + for G2 in glist: + if isomorphic(G1, G2): + return True + return False + + +if __name__ == '__main__': + G=atlas6() + + print("graph has %d nodes with %d edges"\ + %(nx.number_of_nodes(G), nx.number_of_edges(G))) + print(nx.number_connected_components(G), "connected components") + + try: + import pygraphviz + from networkx.drawing.nx_agraph import graphviz_layout + except ImportError: + try: + import pydotplus + from networkx.drawing.nx_pydot import graphviz_layout + except ImportError: + raise ImportError("This example needs Graphviz and either " + "PyGraphviz or PyDotPlus") + + import matplotlib.pyplot as plt + plt.figure(1, figsize=(8, 8)) + # layout graphs with positions using graphviz neato + pos = graphviz_layout(G, prog="neato") + # color nodes the same in each connected subgraph + C = nx.connected_component_subgraphs(G) + for g in C: + c = [random.random()] * nx.number_of_nodes(g) # random color... + nx.draw(g, + pos, + node_size=40, + node_color=c, + vmin=0.0, + vmax=1.0, + with_labels=False + ) + plt.savefig("atlas.png", dpi=75) diff --git a/share/doc/networkx-1.11/examples/graph/atlas2.py b/share/doc/networkx-1.11/examples/graph/atlas2.py new file mode 100644 index 000000000..268f5d3a6 --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/atlas2.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +""" +Write first 20 graphs from the graph atlas as graphviz dot files +Gn.dot where n=0,19. +Requires pygraphviz and graphviz. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) +# Date: 2005-05-19 14:23:02 -0600 (Thu, 19 May 2005) + +# Copyright (C) 2006-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx +from networkx.generators.atlas import * +from pygraphviz import * + +atlas = graph_atlas_g()[0:20] + + +for G in atlas: + print("graph %s has %d nodes with %d edges" + %(G.name,NX.number_of_nodes(G),NX.number_of_edges(G))) + A = nx.nx_agraph.to_agraph(G) + A.graph_attr['label'] = G.name + # set default node attributes + A.node_attr['color'] = 'red' + A.node_attr['style'] = 'filled' + A.node_attr['shape'] = 'circle' + A.write(G.name + '.dot') diff --git a/share/doc/networkx-1.11/examples/graph/degree_sequence.py b/share/doc/networkx-1.11/examples/graph/degree_sequence.py new file mode 100644 index 000000000..8a767d293 --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/degree_sequence.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +""" +Random graph from given degree sequence. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) +# Date: 2004-11-03 08:11:09 -0700 (Wed, 03 Nov 2004) +# Revision: 503 + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +from networkx import * + +z=[5,3,3,3,3,2,2,2,1,1,1] +print(is_valid_degree_sequence(z)) + +print("Configuration model") +G=configuration_model(z) # configuration model +degree_sequence=list(degree(G).values()) # degree sequence +print("Degree sequence %s" % degree_sequence) +print("Degree histogram") +hist={} +for d in degree_sequence: + if d in hist: + hist[d]+=1 + else: + hist[d]=1 +print("degree #nodes") +for d in hist: + print('%d %d' % (d,hist[d])) diff --git a/share/doc/networkx-1.11/examples/graph/erdos_renyi.py b/share/doc/networkx-1.11/examples/graph/erdos_renyi.py new file mode 100644 index 000000000..63ffb1891 --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/erdos_renyi.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +#!/usr/bin/env python +""" +Create an G{n,m} random graph with n nodes and m edges +and report some properties. + +This graph is sometimes called the Erdős-Rényi graph +but is different from G{n,p} or binomial_graph which is also +sometimes called the Erdős-Rényi graph. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +from networkx import * +import sys + +n=10 # 10 nodes +m=20 # 20 edges + +G=gnm_random_graph(n,m) + +# some properties +print("node degree clustering") +for v in nodes(G): + print('%s %d %f' % (v,degree(G,v),clustering(G,v))) + +# print the adjacency list to terminal +try: + write_adjlist(G,sys.stdout) +except TypeError: # Python 3.x + write_adjlist(G,sys.stdout.buffer) + diff --git a/share/doc/networkx-1.11/examples/graph/expected_degree_sequence.py b/share/doc/networkx-1.11/examples/graph/expected_degree_sequence.py new file mode 100644 index 000000000..0a00851cd --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/expected_degree_sequence.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +""" +Random graph from given degree sequence. +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2006-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +from networkx import * +from networkx.generators.degree_seq import * + +# make a random graph of 500 nodes with expected degrees of 50 +n=500 # n nodes +p=0.1 +w=[p*n for i in range(n)] # w = p*n for all nodes +G=expected_degree_graph(w) # configuration model +print("Degree histogram") +print("degree (#nodes) ****") +dh=degree_histogram(G) +low=min(degree(G)) +for i in range(low,len(dh)): + bar=''.join(dh[i]*['*']) + print("%2s (%2s) %s"%(i,dh[i],bar)) + diff --git a/share/doc/networkx-1.11/examples/graph/football.py b/share/doc/networkx-1.11/examples/graph/football.py new file mode 100644 index 000000000..d70cebc6c --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/football.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +""" +Load football network in GML format and compute some network statistcs. + +Shows how to download GML graph in a zipped file, unpack it, and load +into a NetworkX graph. + +Requires Internet connection to download the URL +http://www-personal.umich.edu/~mejn/netdata/football.zip + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2007-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +from networkx import * + +url="http://www-personal.umich.edu/~mejn/netdata/football.zip" + +try: # Python 3.x + import urllib.request as urllib +except ImportError: # Python 2.x + import urllib +import io +import zipfile + +sock = urllib.urlopen(url) # open URL +s=io.BytesIO(sock.read()) # read into BytesIO "file" +sock.close() + +zf = zipfile.ZipFile(s) # zipfile object +txt=zf.read('football.txt').decode() # read info file +gml=zf.read('football.gml').decode() # read gml data +# throw away bogus first line with # from mejn files +gml=gml.split('\n')[1:] +G=parse_gml(gml) # parse gml data + +print(txt) +# print degree for each team - number of games +for n,d in G.degree_iter(): + print('%s %d' % (n, d)) diff --git a/share/doc/networkx-1.11/examples/graph/karate_club.py b/share/doc/networkx-1.11/examples/graph/karate_club.py new file mode 100644 index 000000000..58121f3c7 --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/karate_club.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python +""" +Zachary's Karate Club graph + +Data file from: +http://vlado.fmf.uni-lj.si/pub/networks/data/Ucinet/UciData.htm + +Reference: +Zachary W. (1977). +An information flow model for conflict and fission in small groups. +Journal of Anthropological Research, 33, 452-473. +""" +import networkx as nx +G=nx.karate_club_graph() +print("Node Degree") +for v in G: + print('%s %s' % (v,G.degree(v))) diff --git a/share/doc/networkx-1.11/examples/graph/knuth_miles.py b/share/doc/networkx-1.11/examples/graph/knuth_miles.py new file mode 100644 index 000000000..0ba9fd767 --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/knuth_miles.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +""" +An example using networkx.Graph(). + +miles_graph() returns an undirected graph over the 128 US cities from +the datafile miles_dat.txt. The cities each have location and population +data. The edges are labeled with the distance betwen the two cities. + +This example is described in Section 1.1 in Knuth's book [1,2]. + +References. +----------- + +[1] Donald E. Knuth, + "The Stanford GraphBase: A Platform for Combinatorial Computing", + ACM Press, New York, 1993. +[2] http://www-cs-faculty.stanford.edu/~knuth/sgb.html + + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx + + +def miles_graph(): + """ Return the cites example graph in miles_dat.txt + from the Stanford GraphBase. + """ + # open file miles_dat.txt.gz (or miles_dat.txt) + import gzip + fh = gzip.open('knuth_miles.txt.gz','r') + + G=nx.Graph() + G.position={} + G.population={} + + cities=[] + for line in fh.readlines(): + line = line.decode() + if line.startswith("*"): # skip comments + continue + + numfind=re.compile("^\d+") + + if numfind.match(line): # this line is distances + dist=line.split() + for d in dist: + G.add_edge(city,cities[i],weight=int(d)) + i=i+1 + else: # this line is a city, position, population + i=1 + (city,coordpop)=line.split("[") + cities.insert(0,city) + (coord,pop)=coordpop.split("]") + (y,x)=coord.split(",") + + G.add_node(city) + # assign position - flip x axis for matplotlib, shift origin + G.position[city]=(-int(x)+7500,int(y)-3000) + G.population[city]=float(pop)/1000.0 + return G + +if __name__ == '__main__': + import networkx as nx + import re + import sys + + G=miles_graph() + + print("Loaded miles_dat.txt containing 128 cities.") + print("digraph has %d nodes with %d edges"\ + %(nx.number_of_nodes(G),nx.number_of_edges(G))) + + + # make new graph of cites, edge if less then 300 miles between them + H=nx.Graph() + for v in G: + H.add_node(v) + for (u,v,d) in G.edges(data=True): + if d['weight'] < 300: + H.add_edge(u,v) + + # draw with matplotlib/pylab + + try: + import matplotlib.pyplot as plt + plt.figure(figsize=(8,8)) + # with nodes colored by degree sized by population + node_color=[float(H.degree(v)) for v in H] + nx.draw(H,G.position, + node_size=[G.population[v] for v in H], + node_color=node_color, + with_labels=False) + + # scale the axes equally + plt.xlim(-5000,500) + plt.ylim(-2000,3500) + + plt.savefig("knuth_miles.png") + except: + pass + + + diff --git a/share/doc/networkx-1.11/examples/graph/knuth_miles.txt.gz b/share/doc/networkx-1.11/examples/graph/knuth_miles.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..62b7f95fb8aebea1f93b55924697e2a1958d3b13 GIT binary patch literal 20317 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 A*#H0l literal 0 HcmV?d00001 diff --git a/share/doc/networkx-1.11/examples/graph/napoleon_russian_campaign.py b/share/doc/networkx-1.11/examples/graph/napoleon_russian_campaign.py new file mode 100644 index 000000000..bdc8e918f --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/napoleon_russian_campaign.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python +""" +Minard's data from Napoleon's 1812-1813 Russian Campaign. +http://www.math.yorku.ca/SCS/Gallery/minard/minard.txt + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2006-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import string +import networkx as nx + + +def minard_graph(): + data1="""\ +24.0,54.9,340000,A,1 +24.5,55.0,340000,A,1 +25.5,54.5,340000,A,1 +26.0,54.7,320000,A,1 +27.0,54.8,300000,A,1 +28.0,54.9,280000,A,1 +28.5,55.0,240000,A,1 +29.0,55.1,210000,A,1 +30.0,55.2,180000,A,1 +30.3,55.3,175000,A,1 +32.0,54.8,145000,A,1 +33.2,54.9,140000,A,1 +34.4,55.5,127100,A,1 +35.5,55.4,100000,A,1 +36.0,55.5,100000,A,1 +37.6,55.8,100000,A,1 +37.7,55.7,100000,R,1 +37.5,55.7,98000,R,1 +37.0,55.0,97000,R,1 +36.8,55.0,96000,R,1 +35.4,55.3,87000,R,1 +34.3,55.2,55000,R,1 +33.3,54.8,37000,R,1 +32.0,54.6,24000,R,1 +30.4,54.4,20000,R,1 +29.2,54.3,20000,R,1 +28.5,54.2,20000,R,1 +28.3,54.3,20000,R,1 +27.5,54.5,20000,R,1 +26.8,54.3,12000,R,1 +26.4,54.4,14000,R,1 +25.0,54.4,8000,R,1 +24.4,54.4,4000,R,1 +24.2,54.4,4000,R,1 +24.1,54.4,4000,R,1""" + data2="""\ +24.0,55.1,60000,A,2 +24.5,55.2,60000,A,2 +25.5,54.7,60000,A,2 +26.6,55.7,40000,A,2 +27.4,55.6,33000,A,2 +28.7,55.5,33000,R,2 +29.2,54.2,30000,R,2 +28.5,54.1,30000,R,2 +28.3,54.2,28000,R,2""" + data3="""\ +24.0,55.2,22000,A,3 +24.5,55.3,22000,A,3 +24.6,55.8,6000,A,3 +24.6,55.8,6000,R,3 +24.2,54.4,6000,R,3 +24.1,54.4,6000,R,3""" + cities="""\ +24.0,55.0,Kowno +25.3,54.7,Wilna +26.4,54.4,Smorgoni +26.8,54.3,Moiodexno +27.7,55.2,Gloubokoe +27.6,53.9,Minsk +28.5,54.3,Studienska +28.7,55.5,Polotzk +29.2,54.4,Bobr +30.2,55.3,Witebsk +30.4,54.5,Orscha +30.4,53.9,Mohilow +32.0,54.8,Smolensk +33.2,54.9,Dorogobouge +34.3,55.2,Wixma +34.4,55.5,Chjat +36.0,55.5,Mojaisk +37.6,55.8,Moscou +36.6,55.3,Tarantino +36.5,55.0,Malo-Jarosewii""" + + c={} + for line in cities.split('\n'): + x,y,name=line.split(',') + c[name]=(float(x),float(y)) + + g=[] + + for data in [data1,data2,data3]: + G=nx.Graph() + i=0 + G.pos={} # location + G.pop={} # size + last=None + for line in data.split('\n'): + x,y,p,r,n=line.split(',') + G.pos[i]=(float(x),float(y)) + G.pop[i]=int(p) + if last is None: + last=i + else: + G.add_edge(i,last,{r:int(n)}) + last=i + i=i+1 + g.append(G) + + return g,c + +if __name__ == "__main__": + + (g,city)=minard_graph() + + try: + import matplotlib.pyplot as plt + plt.figure(1,figsize=(11,5)) + plt.clf() + colors=['b','g','r'] + for G in g: + c=colors.pop(0) + node_size=[int(G.pop[n]/300.0) for n in G] + nx.draw_networkx_edges(G,G.pos,edge_color=c,width=4,alpha=0.5) + nx.draw_networkx_nodes(G,G.pos,node_size=node_size,node_color=c,alpha=0.5) + nx.draw_networkx_nodes(G,G.pos,node_size=5,node_color='k') + + for c in city: + x,y=city[c] + plt.text(x,y+0.1,c) + plt.savefig("napoleon_russian_campaign.png") + except ImportError: + pass + diff --git a/share/doc/networkx-1.11/examples/graph/roget.py b/share/doc/networkx-1.11/examples/graph/roget.py new file mode 100644 index 000000000..b31baa322 --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/roget.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +""" +Build a directed graph of 1022 categories and +5075 cross-references as defined in the 1879 version of Roget's Thesaurus +contained in the datafile roget_dat.txt. This example is described in +Section 1.2 in Knuth's book [1,2]. + +Note that one of the 5075 cross references is a self loop yet +it is included in the graph built here because +the standard networkx DiGraph class allows self loops. +(cf. 400pungency:400 401 403 405). + +References. +---------- + +[1] Donald E. Knuth, + "The Stanford GraphBase: A Platform for Combinatorial Computing", + ACM Press, New York, 1993. +[2] http://www-cs-faculty.stanford.edu/~knuth/sgb.html + + +""" +from __future__ import print_function +# Authors: Brendt Wohlberg, Aric Hagberg (hagberg@lanl.gov) +# Date: 2005-04-01 07:56:22 -0700 (Fri, 01 Apr 2005) + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +from networkx import * +import re +import sys + +def roget_graph(): + """ Return the thesaurus graph from the roget.dat example in + the Stanford Graph Base. + """ + # open file roget_dat.txt.gz (or roget_dat.txt) + import gzip + fh=gzip.open('roget_dat.txt.gz','r') + + G=DiGraph() + + for line in fh.readlines(): + line = line.decode() + if line.startswith("*"): # skip comments + continue + if line.startswith(" "): # this is a continuation line, append + line=oldline+line + if line.endswith("\\\n"): # continuation line, buffer, goto next + oldline=line.strip("\\\n") + continue + + (headname,tails)=line.split(":") + + # head + numfind=re.compile("^\d+") # re to find the number of this word + head=numfind.findall(headname)[0] # get the number + + G.add_node(head) + + for tail in tails.split(): + if head==tail: + print("skipping self loop",head,tail, file=sys.stderr) + G.add_edge(head,tail) + + return G + +if __name__ == '__main__': + from networkx import * + G=roget_graph() + print("Loaded roget_dat.txt containing 1022 categories.") + print("digraph has %d nodes with %d edges"\ + %(number_of_nodes(G),number_of_edges(G))) + UG=G.to_undirected() + print(number_connected_components(UG),"connected components") + diff --git a/share/doc/networkx-1.11/examples/graph/roget_dat.txt.gz b/share/doc/networkx-1.11/examples/graph/roget_dat.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..6552465dead35f3141227404d8925af0873a643c GIT binary patch literal 15758 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 +Subject: NetworkX +Date: Thu, 16 Jun 2005 16:12:13 -0700 +To: Bob +Status: RO +Content-Length: 86 +Lines: 5 + +Bob, check out the new networkx release - you and +Carol might really like it. + +Alice + + +From bob@gov Thu Jun 16 18:13:12 2005 +Return-Path: +Subject: Re: NetworkX +From: Bob +To: Alice +Content-Type: text/plain +Date: Thu, 16 Jun 2005 18:13:12 -0700 +Status: RO +Content-Length: 26 +Lines: 4 + +Thanks for the tip. + +Bob + + +From ted@com Thu Jul 28 09:53:31 2005 +Return-Path: +Subject: Graph package in Python? +From: Ted +To: Bob +Content-Type: text/plain +Date: Thu, 28 Jul 2005 09:47:03 -0700 +Status: RO +Content-Length: 90 +Lines: 3 + +Hey Ted - I'm looking for a Python package for +graphs and networks. Do you know of any? + + +From bob@gov Thu Jul 28 09:59:31 2005 +Return-Path: +Subject: Re: Graph package in Python? +From: Bob +To: Ted +Content-Type: text/plain +Date: Thu, 28 Jul 2005 09:59:03 -0700 +Status: RO +Content-Length: 180 +Lines: 9 + + +Check out the NetworkX package - Alice sent me the tip! + +Bob + +>> bob@gov scrawled: +>> Hey Ted - I'm looking for a Python package for +>> graphs and networks. Do you know of any? + + +From ted@com Thu Jul 28 15:53:31 2005 +Return-Path: +Subject: get together for lunch to discuss Networks? +From: Ted +To: Bob , Carol , Alice +Content-Type: text/plain +Date: Thu, 28 Jul 2005 15:47:03 -0700 +Status: RO +Content-Length: 139 +Lines: 5 + +Hey everyrone! Want to meet at that restaurant on the +island in Konigsburg tonight? Bring your laptops +and we can install NetworkX. + +Ted + diff --git a/share/doc/networkx-1.11/examples/graph/unix_email.py b/share/doc/networkx-1.11/examples/graph/unix_email.py new file mode 100644 index 000000000..ae95b729a --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/unix_email.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +""" +Create a directed graph, allowing multiple edges and self loops, from +a unix mailbox. The nodes are email addresses with links +that point from the sender to the recievers. The edge data +is a Python email.Message object which contains all of +the email message data. + +This example shows the power of XDiGraph to hold edge data +of arbitrary Python objects (in this case a list of email messages). + +By default, load the sample unix email mailbox called "unix_email.mbox". +You can load your own mailbox by naming it on the command line, eg + +python unixemail.py /var/spool/mail/username + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2005-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import email +from email.utils import getaddresses,parseaddr +import mailbox +import sys + +# unix mailbox recipe +# see http://www.python.org/doc/current/lib/module-mailbox.html +def msgfactory(fp): + try: + return email.message_from_file(fp) + except email.Errors.MessageParseError: + # Don't return None since that will stop the mailbox iterator + return '' + + + +if __name__ == '__main__': + + import networkx as nx + try: + import matplotlib.pyplot as plt + except: + pass + + if len(sys.argv)==1: + filePath = "unix_email.mbox" + else: + filePath = sys.argv[1] + + mbox = mailbox.mbox(filePath, msgfactory) # parse unix mailbox + + G=nx.MultiDiGraph() # create empty graph + + # parse each messages and build graph + for msg in mbox: # msg is python email.Message.Message object + (source_name,source_addr) = parseaddr(msg['From']) # sender + # get all recipients + # see http://www.python.org/doc/current/lib/module-email.Utils.html + tos = msg.get_all('to', []) + ccs = msg.get_all('cc', []) + resent_tos = msg.get_all('resent-to', []) + resent_ccs = msg.get_all('resent-cc', []) + all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs) + # now add the edges for this mail message + for (target_name,target_addr) in all_recipients: + G.add_edge(source_addr,target_addr,message=msg) + + # print edges with message subject + for (u,v,d) in G.edges_iter(data=True): + print("From: %s To: %s Subject: %s"%(u,v,d['message']["Subject"])) + + + try: # draw + pos=nx.spring_layout(G,iterations=10) + nx.draw(G,pos,node_size=0,alpha=0.4,edge_color='r',font_size=16) + plt.savefig("unix_email.png") + plt.show() + except: # matplotlib not available + pass diff --git a/share/doc/networkx-1.11/examples/graph/words.py b/share/doc/networkx-1.11/examples/graph/words.py new file mode 100644 index 000000000..9b5b9dfcf --- /dev/null +++ b/share/doc/networkx-1.11/examples/graph/words.py @@ -0,0 +1,84 @@ +""" +Words/Ladder Graph +------------------ +Generate an undirected graph over the 5757 5-letter words in the +datafile words_dat.txt.gz. Two words are connected by an edge +if they differ in one letter, resulting in 14,135 edges. This example +is described in Section 1.1 in Knuth's book [1]_,[2]_. + +References +---------- +.. [1] Donald E. Knuth, + "The Stanford GraphBase: A Platform for Combinatorial Computing", + ACM Press, New York, 1993. +.. [2] http://www-cs-faculty.stanford.edu/~knuth/sgb.html +""" +# Authors: Aric Hagberg (hagberg@lanl.gov), +# Brendt Wohlberg, +# hughdbrown@yahoo.com + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx + +#------------------------------------------------------------------- +# The Words/Ladder graph of Section 1.1 +#------------------------------------------------------------------- +def generate_graph(words): + from string import ascii_lowercase as lowercase + G = nx.Graph(name="words") + lookup = dict((c,lowercase.index(c)) for c in lowercase) + def edit_distance_one(word): + for i in range(len(word)): + left, c, right = word[0:i], word[i], word[i+1:] + j = lookup[c] # lowercase.index(c) + for cc in lowercase[j+1:]: + yield left + cc + right + candgen = ((word, cand) for word in sorted(words) + for cand in edit_distance_one(word) if cand in words) + G.add_nodes_from(words) + for word, cand in candgen: + G.add_edge(word, cand) + return G + +def words_graph(): + """Return the words example graph from the Stanford GraphBase""" + import gzip + fh=gzip.open('words_dat.txt.gz','r') + words=set() + for line in fh.readlines(): + line = line.decode() + if line.startswith('*'): + continue + w=str(line[0:5]) + words.add(w) + return generate_graph(words) + +if __name__ == '__main__': + from networkx import * + G=words_graph() + print("Loaded words_dat.txt containing 5757 five-letter English words.") + print("Two words are connected if they differ in one letter.") + print("Graph has %d nodes with %d edges" + %(number_of_nodes(G),number_of_edges(G))) + print("%d connected components" % number_connected_components(G)) + + for (source,target) in [('chaos','order'), + ('nodes','graph'), + ('pound','marks')]: + print("Shortest path between %s and %s is"%(source,target)) + try: + sp=shortest_path(G, source, target) + for n in sp: + print(n) + except nx.NetworkXNoPath: + print("None") + + + + diff --git a/share/doc/networkx-1.11/examples/graph/words_dat.txt.gz b/share/doc/networkx-1.11/examples/graph/words_dat.txt.gz new file mode 100644 index 0000000000000000000000000000000000000000..78aff7913e6048eb7ed30fca14df42b34643ab53 GIT binary patch literal 33695 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 TaVzJ4`}qF=34&oLgB1Y)QF@!O literal 0 HcmV?d00001 diff --git a/share/doc/networkx-1.11/examples/multigraph/chess_masters.py b/share/doc/networkx-1.11/examples/multigraph/chess_masters.py new file mode 100644 index 000000000..76e8ae6f7 --- /dev/null +++ b/share/doc/networkx-1.11/examples/multigraph/chess_masters.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python + +""" +An example of the MultiDiGraph clas + +The function chess_pgn_graph reads a collection of chess +matches stored in the specified PGN file +(PGN ="Portable Game Notation") +Here the (compressed) default file --- + chess_masters_WCC.pgn.bz2 --- +contains all 685 World Chess Championship matches +from 1886 - 1985. +(data from http://chessproblem.my-free-games.com/chess/games/Download-PGN.php) + +The chess_pgn_graph() function returns a MultiDiGraph +with multiple edges. Each node is +the last name of a chess master. Each edge is directed +from white to black and contains selected game info. + +The key statement in chess_pgn_graph below is + G.add_edge(white, black, game_info) +where game_info is a dict describing each game. + +""" +# Copyright (C) 2006-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx + +# tag names specifying what game info should be +# stored in the dict on each digraph edge +game_details=["Event", + "Date", + "Result", + "ECO", + "Site"] + +def chess_pgn_graph(pgn_file="chess_masters_WCC.pgn.bz2"): + """Read chess games in pgn format in pgn_file. + + Filenames ending in .gz or .bz2 will be uncompressed. + + Return the MultiDiGraph of players connected by a chess game. + Edges contain game data in a dict. + + """ + import bz2 + G=nx.MultiDiGraph() + game={} + datafile = bz2.BZ2File(pgn_file) + lines = (line.decode().rstrip('\r\n') for line in datafile) + for line in lines: + if line.startswith('['): + tag,value=line[1:-1].split(' ',1) + game[str(tag)]=value.strip('"') + else: + # empty line after tag set indicates + # we finished reading game info + if game: + white=game.pop('White') + black=game.pop('Black') + G.add_edge(white, black, **game) + game={} + return G + + +if __name__ == '__main__': + G=chess_pgn_graph() + + ngames=G.number_of_edges() + nplayers=G.number_of_nodes() + + print("Loaded %d chess games between %d players\n"\ + % (ngames,nplayers)) + + # identify connected components + # of the undirected version + Gcc=list(nx.connected_component_subgraphs(G.to_undirected())) + if len(Gcc)>1: + print("Note the disconnected component consisting of:") + print(Gcc[1].nodes()) + + # find all games with B97 opening (as described in ECO) + openings=set([game_info['ECO'] + for (white,black,game_info) in G.edges(data=True)]) + print("\nFrom a total of %d different openings,"%len(openings)) + print('the following games used the Sicilian opening') + print('with the Najdorff 7...Qb6 "Poisoned Pawn" variation.\n') + + for (white,black,game_info) in G.edges(data=True): + if game_info['ECO']=='B97': + print(white,"vs",black) + for k,v in game_info.items(): + print(" ",k,": ",v) + print("\n") + + + try: + import matplotlib.pyplot as plt + except ImportError: + import sys + print("Matplotlib needed for drawing. Skipping") + sys.exit(0) + + # make new undirected graph H without multi-edges + H=nx.Graph(G) + + # edge width is proportional number of games played + edgewidth=[] + for (u,v,d) in H.edges(data=True): + edgewidth.append(len(G.get_edge_data(u,v))) + + # node size is proportional to number of games won + wins=dict.fromkeys(G.nodes(),0.0) + for (u,v,d) in G.edges(data=True): + r=d['Result'].split('-') + if r[0]=='1': + wins[u]+=1.0 + elif r[0]=='1/2': + wins[u]+=0.5 + wins[v]+=0.5 + else: + wins[v]+=1.0 + try: + pos=nx.nx_agraph.graphviz_layout(H) + except: + pos=nx.spring_layout(H,iterations=20) + + plt.rcParams['text.usetex'] = False + plt.figure(figsize=(8,8)) + nx.draw_networkx_edges(H,pos,alpha=0.3,width=edgewidth, edge_color='m') + nodesize=[wins[v]*50 for v in H] + nx.draw_networkx_nodes(H,pos,node_size=nodesize,node_color='w',alpha=0.4) + nx.draw_networkx_edges(H,pos,alpha=0.4,node_size=0,width=1,edge_color='k') + nx.draw_networkx_labels(H,pos,fontsize=14) + font = {'fontname' : 'Helvetica', + 'color' : 'k', + 'fontweight' : 'bold', + 'fontsize' : 14} + plt.title("World Chess Championship Games: 1886 - 1985", font) + + # change font and write text (using data coordinates) + font = {'fontname' : 'Helvetica', + 'color' : 'r', + 'fontweight' : 'bold', + 'fontsize' : 14} + + plt.text(0.5, 0.97, "edge width = # games played", + horizontalalignment='center', + transform=plt.gca().transAxes) + plt.text(0.5, 0.94, "node size = # games won", + horizontalalignment='center', + transform=plt.gca().transAxes) + + plt.axis('off') + plt.savefig("chess_masters.png",dpi=75) + print("Wrote chess_masters.png") + plt.show() # display diff --git a/share/doc/networkx-1.11/examples/multigraph/chess_masters_WCC.pgn.bz2 b/share/doc/networkx-1.11/examples/multigraph/chess_masters_WCC.pgn.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..3761ce5280c7a4906b04fab66d422cf92ab567be GIT binary patch literal 100224 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 BrKJD> literal 0 HcmV?d00001 diff --git a/share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_attributes.py b/share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_attributes.py new file mode 100644 index 000000000..0279569ac --- /dev/null +++ b/share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_attributes.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +""" +An example showing how to use the interface to the pygraphviz +AGraph class to convert to and from graphviz. + +Also see the pygraphviz documentation and examples at +http://pygraphviz.github.io/ + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2006-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx + +# networkx graph +G = nx.Graph() +# ad edges with red color +G.add_edge(1, 2, color='red') +G.add_edge(2, 3, color='red') +# add nodes 3 and 4 +G.add_node(3) +G.add_node(4) + +# convert to a graphviz agraph +A = nx.nx_agraph.to_agraph(G) + +# write to dot file +A.write('k5_attributes.dot') + +# convert back to networkx Graph with attributes on edges and +# default attributes as dictionary data +X = nx.nx_agraph.from_agraph(A) +print("edges") +print(X.edges(data=True)) +print("default graph attributes") +print(X.graph) +print("node node attributes") +print(X.node) diff --git a/share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_draw.py b/share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_draw.py new file mode 100644 index 000000000..8f361eb22 --- /dev/null +++ b/share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_draw.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +""" +An example showing how to use the interface to the pygraphviz +AGraph class to draw a graph. + +Also see the pygraphviz documentation and examples at +http://pygraphviz.github.io/ + + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2006-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx + +# plain graph + +G = nx.complete_graph(5) # start with K5 in networkx +A = nx.nx_agraph.to_agraph(G) # convert to a graphviz graph +A.layout() # neato layout +A.draw("k5.ps") # write postscript in k5.ps with neato layout + diff --git a/share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_simple.py b/share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_simple.py new file mode 100644 index 000000000..7fb4ad421 --- /dev/null +++ b/share/doc/networkx-1.11/examples/pygraphviz/pygraphviz_simple.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +""" +An example showing how to use the interface to the pygraphviz +AGraph class to convert to and from graphviz. + +Also see the pygraphviz documentation and examples at +http://pygraphviz.github.io/ + + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2006-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx + +# plain graph + +G = nx.complete_graph(5) # start with K5 in networkx +A = nx.nx_agraph.to_agraph(G) # convert to a graphviz graph +X1 = nx.nx_agraph.from_agraph(A) # convert back to networkx (but as Graph) +X2 = nx.Graph(A) # fancy way to do conversion +G1 = nx.Graph(X1) # now make it a Graph + +A.write('k5.dot') # write to dot file +X3 = nx.nx_agraph.read_dot('k5.dot') # read from dotfile + diff --git a/share/doc/networkx-1.11/examples/pygraphviz/write_dotfile.py b/share/doc/networkx-1.11/examples/pygraphviz/write_dotfile.py new file mode 100644 index 000000000..5d41ff0d5 --- /dev/null +++ b/share/doc/networkx-1.11/examples/pygraphviz/write_dotfile.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +""" +Write a dot file from a networkx graph for further processing with graphviz. + +You need to have either pygraphviz or pydotplus for this example. + +See http://networkx.github.io/documentation/latest/reference/drawing.html +for more info. + +""" +# Author: Aric Hagberg (hagberg@lanl.gov) + +# Copyright (C) 2004-2016 by +# Aric Hagberg +# Dan Schult +# Pieter Swart +# All rights reserved. +# BSD license. + +import networkx as nx + +# and the following code block is not needed +# but we want to see which module is used and +# if and why it fails +try: + import pygraphviz + from networkx.drawing.nx_agraph import write_dot + print("using package pygraphviz") +except ImportError: + try: + import pydotplus + from networkx.drawing.nx_pydot import write_dot + print("using package pydotplus") + except ImportError: + print() + print("Both pygraphviz and pydotplus were not found ") + print("see http://networkx.github.io/documentation" + "/latest/reference/drawing.html for info") + print() + raise + +G=nx.grid_2d_graph(5,5) # 5x5 grid +write_dot(G,"grid.dot") +print("Now run: neato -Tps grid.dot >grid.ps") From c7bc03f299618c62c5b11caa307601b9123f583a Mon Sep 17 00:00:00 2001 From: Oludare Olugbemi Date: Thu, 6 Jul 2017 09:08:41 -0400 Subject: [PATCH 23/34] Max page size specification for source config endpoint --- api/pagination.py | 2 ++ api/views/sourceConfig.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/api/pagination.py b/api/pagination.py index 0cfdc0d78..d12792e9b 100644 --- a/api/pagination.py +++ b/api/pagination.py @@ -24,6 +24,8 @@ def count(self): class FuzzyPageNumberPagination(PageNumberPagination): django_paginator_class = FuzzyPaginator + max_page_size = 300 + class CursorPagination(CursorPagination): diff --git a/api/views/sourceConfig.py b/api/views/sourceConfig.py index c287701ef..0aec44ddc 100644 --- a/api/views/sourceConfig.py +++ b/api/views/sourceConfig.py @@ -1,10 +1,10 @@ from rest_framework import viewsets from api.views import ShareObjectViewSet - -# trying to fix shit from api.serializers import SourceConfigSerializer from share.models import SourceConfig +from api.pagination import FuzzyPageNumberPagination class SourceConfigViewSet(ShareObjectViewSet): serializer_class = SourceConfigSerializer queryset= SourceConfig.objects.all() + pagination_class = FuzzyPageNumberPagination From f6df317fff92b81052213c458adfcc49b517ccd2 Mon Sep 17 00:00:00 2001 From: Oludare Olugbemi Date: Thu, 6 Jul 2017 09:14:53 -0400 Subject: [PATCH 24/34] Revert "Max page size specification for source config endpoint" This reverts commit c7bc03f299618c62c5b11caa307601b9123f583a. --- api/pagination.py | 2 -- api/views/sourceConfig.py | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/api/pagination.py b/api/pagination.py index d12792e9b..0cfdc0d78 100644 --- a/api/pagination.py +++ b/api/pagination.py @@ -24,8 +24,6 @@ def count(self): class FuzzyPageNumberPagination(PageNumberPagination): django_paginator_class = FuzzyPaginator - max_page_size = 300 - class CursorPagination(CursorPagination): diff --git a/api/views/sourceConfig.py b/api/views/sourceConfig.py index 0aec44ddc..c287701ef 100644 --- a/api/views/sourceConfig.py +++ b/api/views/sourceConfig.py @@ -1,10 +1,10 @@ from rest_framework import viewsets from api.views import ShareObjectViewSet + +# trying to fix shit from api.serializers import SourceConfigSerializer from share.models import SourceConfig -from api.pagination import FuzzyPageNumberPagination class SourceConfigViewSet(ShareObjectViewSet): serializer_class = SourceConfigSerializer queryset= SourceConfig.objects.all() - pagination_class = FuzzyPageNumberPagination From 84d569daff862df73d2822e65da9d87d3f2777b2 Mon Sep 17 00:00:00 2001 From: Emily Carden Date: Thu, 6 Jul 2017 09:45:33 -0400 Subject: [PATCH 25/34] merging --- api/pagination.py | 1 + api/views/sourceConfig.py | 1 + 2 files changed, 2 insertions(+) diff --git a/api/pagination.py b/api/pagination.py index 0cfdc0d78..19861bb8f 100644 --- a/api/pagination.py +++ b/api/pagination.py @@ -24,6 +24,7 @@ def count(self): class FuzzyPageNumberPagination(PageNumberPagination): django_paginator_class = FuzzyPaginator + max_page_size = 300 class CursorPagination(CursorPagination): diff --git a/api/views/sourceConfig.py b/api/views/sourceConfig.py index 0aec44ddc..83bac19b4 100644 --- a/api/views/sourceConfig.py +++ b/api/views/sourceConfig.py @@ -1,5 +1,6 @@ from rest_framework import viewsets from api.views import ShareObjectViewSet + from api.serializers import SourceConfigSerializer from share.models import SourceConfig from api.pagination import FuzzyPageNumberPagination From 716373e4d67e53fef11f5df5b5681b8a5826ec94 Mon Sep 17 00:00:00 2001 From: Emily Carden Date: Mon, 17 Jul 2017 13:22:07 -0400 Subject: [PATCH 26/34] recentHarvest component --- api/views/share.py | 1 - app/components/recent-harvest.js | 4 +++ app/templates/components/recent-harvest.hbs | 1 + .../components/recent-harvest-test.js | 25 +++++++++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 app/components/recent-harvest.js create mode 100644 app/templates/components/recent-harvest.hbs create mode 100644 tests/integration/components/recent-harvest-test.js diff --git a/api/views/share.py b/api/views/share.py index 3a0ed5248..11199bf70 100644 --- a/api/views/share.py +++ b/api/views/share.py @@ -36,7 +36,6 @@ def get_queryset(self, list=True): # Override to convert encoded pk to an actual pk def get_object(self): - import ipdb; ipdb.set_trace() queryset = self.filter_queryset(self.get_queryset(False)) # Perform the lookup filtering. diff --git a/app/components/recent-harvest.js b/app/components/recent-harvest.js new file mode 100644 index 000000000..926b61300 --- /dev/null +++ b/app/components/recent-harvest.js @@ -0,0 +1,4 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({ +}); diff --git a/app/templates/components/recent-harvest.hbs b/app/templates/components/recent-harvest.hbs new file mode 100644 index 000000000..889d9eead --- /dev/null +++ b/app/templates/components/recent-harvest.hbs @@ -0,0 +1 @@ +{{yield}} diff --git a/tests/integration/components/recent-harvest-test.js b/tests/integration/components/recent-harvest-test.js new file mode 100644 index 000000000..27367aacc --- /dev/null +++ b/tests/integration/components/recent-harvest-test.js @@ -0,0 +1,25 @@ +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +moduleForComponent('recent-harvest', 'Integration | Component | recent harvest', { + integration: true +}); + +test('it renders', function(assert) { + + // Set any properties with this.set('myProperty', 'value'); + // Handle any actions with this.on('myAction', function(val) { ... }); + + this.render(hbs`{{recent-harvest}}`); + + assert.equal(this.$().text().trim(), ''); + + // Template block usage: + this.render(hbs` + {{#recent-harvest}} + template block text + {{/recent-harvest}} + `); + + assert.equal(this.$().text().trim(), 'template block text'); +}); From 546b6ec131c9a14a0b512d41ce85079c8d206243 Mon Sep 17 00:00:00 2001 From: Emily Carden Date: Wed, 19 Jul 2017 11:59:22 -0400 Subject: [PATCH 27/34] commit --- api/views/sourceConfig.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/views/sourceConfig.py b/api/views/sourceConfig.py index 83bac19b4..755f33106 100644 --- a/api/views/sourceConfig.py +++ b/api/views/sourceConfig.py @@ -1,6 +1,6 @@ from rest_framework import viewsets from api.views import ShareObjectViewSet - +from rest_framework import filters from api.serializers import SourceConfigSerializer from share.models import SourceConfig from api.pagination import FuzzyPageNumberPagination @@ -9,3 +9,4 @@ class SourceConfigViewSet(ShareObjectViewSet): serializer_class = SourceConfigSerializer queryset= SourceConfig.objects.all() pagination_class = FuzzyPageNumberPagination + From d09273a4e419385f89d8fe28ce2424df94c824a2 Mon Sep 17 00:00:00 2001 From: Emily Carden Date: Wed, 19 Jul 2017 15:41:26 -0400 Subject: [PATCH 28/34] query for statuses --- api/views/harvestlogs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/views/harvestlogs.py b/api/views/harvestlogs.py index 02fa70b62..5e23242d1 100644 --- a/api/views/harvestlogs.py +++ b/api/views/harvestlogs.py @@ -15,6 +15,8 @@ def filter_queryset(self, request, queryset, view): if 'source_config_id' in request.GET: decoded = IDObfuscator.decode_id(request.GET['source_config_id']) return queryset.filter(source_config_id=decoded) + if 'status' in request.GET: + return queryset.filter(status=request.GET['status']) else: return queryset From d7d10aed1147de9baba661b867f07d5afb15f2de Mon Sep 17 00:00:00 2001 From: Emily Carden Date: Fri, 21 Jul 2017 09:41:07 -0400 Subject: [PATCH 29/34] query for multiple parameters --- api/views/harvestlogs.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/api/views/harvestlogs.py b/api/views/harvestlogs.py index 5e23242d1..06d179c9b 100644 --- a/api/views/harvestlogs.py +++ b/api/views/harvestlogs.py @@ -1,26 +1,23 @@ from rest_framework import viewsets from rest_framework import filters +from django_filters.filters import MultipleChoiceFilter from api.views import ShareObjectViewSet from share.util import IDObfuscator from api.serializers import HarvestLogSerializer from share.models import HarvestLog - -class SourceConfigFilterBackend(filters.BaseFilterBackend): - """ - Filter that only allows users to see their own objects. - """ - def filter_queryset(self, request, queryset, view): +class SourceConfigFilterBackend(MultipleChoiceFilter): + def filter_queryset(self, request, queryset, view, conjoined=True): if 'source_config_id' in request.GET: decoded = IDObfuscator.decode_id(request.GET['source_config_id']) - return queryset.filter(source_config_id=decoded) + queryset = queryset.filter(source_config_id=decoded) if 'status' in request.GET: - return queryset.filter(status=request.GET['status']) - else: - return queryset + queryset = queryset.filter(status__in=request.GET.getlist('status')) + return queryset class HarvestLogViewSet(ShareObjectViewSet): serializer_class = HarvestLogSerializer queryset = HarvestLog.objects.all() filter_backends = (SourceConfigFilterBackend, ) + filter_fields = ('status',) From 76a9ae13d4bc48fafa212bcb5b7cb7137ba3c41d Mon Sep 17 00:00:00 2001 From: Emily Carden Date: Wed, 26 Jul 2017 11:14:33 -0400 Subject: [PATCH 30/34] return each hl per sc and and show fails first --- api/views/harvestlogs.py | 1 + api/views/sourceConfig.py | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/api/views/harvestlogs.py b/api/views/harvestlogs.py index 06d179c9b..0ed788fe1 100644 --- a/api/views/harvestlogs.py +++ b/api/views/harvestlogs.py @@ -15,6 +15,7 @@ def filter_queryset(self, request, queryset, view, conjoined=True): if 'status' in request.GET: queryset = queryset.filter(status__in=request.GET.getlist('status')) return queryset + #return queryset.order_by('endDate') class HarvestLogViewSet(ShareObjectViewSet): serializer_class = HarvestLogSerializer diff --git a/api/views/sourceConfig.py b/api/views/sourceConfig.py index 755f33106..8d4db6281 100644 --- a/api/views/sourceConfig.py +++ b/api/views/sourceConfig.py @@ -7,6 +7,8 @@ class SourceConfigViewSet(ShareObjectViewSet): serializer_class = SourceConfigSerializer - queryset= SourceConfig.objects.all() + queryset= SourceConfig.objects.all().order_by('harvest_logs__status') pagination_class = FuzzyPageNumberPagination - + for i in queryset: + print(i) + print("harvest logs:", i.harvest_logs) From b30f9aa1c5f19b8dad261e646875be88edd881a5 Mon Sep 17 00:00:00 2001 From: Emily Carden Date: Wed, 26 Jul 2017 15:39:32 -0400 Subject: [PATCH 31/34] working on sorting by health --- api/views/sourceConfig.py | 23 +++++++++++++++++++---- share/models/logs.py | 3 ++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/api/views/sourceConfig.py b/api/views/sourceConfig.py index 8d4db6281..08baa242e 100644 --- a/api/views/sourceConfig.py +++ b/api/views/sourceConfig.py @@ -4,11 +4,26 @@ from api.serializers import SourceConfigSerializer from share.models import SourceConfig from api.pagination import FuzzyPageNumberPagination +from django.db.models import Count +from django.db.models import Aggregate +from share.models import HarvestLog +from django.db.models import OuterRef, Subquery class SourceConfigViewSet(ShareObjectViewSet): serializer_class = SourceConfigSerializer - queryset= SourceConfig.objects.all().order_by('harvest_logs__status') pagination_class = FuzzyPageNumberPagination - for i in queryset: - print(i) - print("harvest logs:", i.harvest_logs) + + def get_queryset(self): + recent_harvests = HarvestLog.objects.filter( + source_config_id = OuterRef(OuterRef('id')), + status__in = [HarvestLog.STATUS.failed, HarvestLog.STATUS.succeeded] + ).order_by('-date_started') + recent_fails = HarvestLog.objects.filter( + status = HarvestLog.STATUS.failed, + id__in = Subquery(recent_harvests.values('id')) + ) + queryset = SourceConfig.objects.all().annotate( + fails=Count(Subquery(recent_fails.values('id'))) + ) + queryset = queryset.order_by('fails') + return queryset diff --git a/share/models/logs.py b/share/models/logs.py index c63a8f6cd..810eda7ca 100644 --- a/share/models/logs.py +++ b/share/models/logs.py @@ -188,7 +188,8 @@ class SkipReasons(enum.Enum): comprised = 'Comprised of succeeded tasks' obsolete = 'Obsolete' - task_id = models.UUIDField(null=True) + # task_id = models.UUIDField(null=True) + task_id = models.UUIDField(null=True, blank=True) status = models.IntegerField(db_index=True, choices=STATUS, default=STATUS.created) context = models.TextField(blank=True, default='') From 74f9804409934fc61396467e990110a06dd2aece Mon Sep 17 00:00:00 2001 From: Emily Carden Date: Thu, 27 Jul 2017 09:14:10 -0400 Subject: [PATCH 32/34] working on sorting by health, issue with OuterRef(OuterRef('id')) --- api/views/sourceConfig.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/api/views/sourceConfig.py b/api/views/sourceConfig.py index 08baa242e..39cc115e3 100644 --- a/api/views/sourceConfig.py +++ b/api/views/sourceConfig.py @@ -8,6 +8,7 @@ from django.db.models import Aggregate from share.models import HarvestLog from django.db.models import OuterRef, Subquery +from django.db.models import IntegerField class SourceConfigViewSet(ShareObjectViewSet): serializer_class = SourceConfigSerializer @@ -17,13 +18,15 @@ def get_queryset(self): recent_harvests = HarvestLog.objects.filter( source_config_id = OuterRef(OuterRef('id')), status__in = [HarvestLog.STATUS.failed, HarvestLog.STATUS.succeeded] - ).order_by('-date_started') + ).order_by('-date_started')[:10] recent_fails = HarvestLog.objects.filter( status = HarvestLog.STATUS.failed, - id__in = Subquery(recent_harvests.values('id')) - ) + id__in = Subquery(recent_harvests.values('id')), + # output_field = models.IntegerField()) + ) queryset = SourceConfig.objects.all().annotate( - fails=Count(Subquery(recent_fails.values('id'))) + fails=Count(Subquery(recent_fails.values('id')), + # output_field = models.IntegerField())) ) queryset = queryset.order_by('fails') return queryset From 41e5c29f89b87a0526f5045679d8b24efe3e323e Mon Sep 17 00:00:00 2001 From: Emily Carden Date: Wed, 2 Aug 2017 14:27:48 -0400 Subject: [PATCH 33/34] commented out sort by health --- api/views/sourceConfig.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/api/views/sourceConfig.py b/api/views/sourceConfig.py index 39cc115e3..5c5f67689 100644 --- a/api/views/sourceConfig.py +++ b/api/views/sourceConfig.py @@ -13,20 +13,21 @@ class SourceConfigViewSet(ShareObjectViewSet): serializer_class = SourceConfigSerializer pagination_class = FuzzyPageNumberPagination + queryset = SourceConfig.objects.all() - def get_queryset(self): - recent_harvests = HarvestLog.objects.filter( - source_config_id = OuterRef(OuterRef('id')), - status__in = [HarvestLog.STATUS.failed, HarvestLog.STATUS.succeeded] - ).order_by('-date_started')[:10] - recent_fails = HarvestLog.objects.filter( - status = HarvestLog.STATUS.failed, - id__in = Subquery(recent_harvests.values('id')), - # output_field = models.IntegerField()) - ) - queryset = SourceConfig.objects.all().annotate( - fails=Count(Subquery(recent_fails.values('id')), - # output_field = models.IntegerField())) - ) - queryset = queryset.order_by('fails') - return queryset + # def get_queryset(self): + # recent_harvests = HarvestLog.objects.filter( + # source_config_id = OuterRef(OuterRef('id')), + # status__in = [HarvestLog.STATUS.failed, HarvestLog.STATUS.succeeded] + # ).order_by('-date_started')[:10] + # recent_fails = HarvestLog.objects.filter( + # status = HarvestLog.STATUS.failed, + # id__in = Subquery(recent_harvests.values('id')), + # # output_field = models.IntegerField()) + # ) + # queryset = SourceConfig.objects.all().annotate( + # fails=Count(Subquery(recent_fails.values('id')), + # # output_field = models.IntegerField())) + # ) + # queryset = queryset.order_by('fails') + # return queryset From c051d0d0db164c443555422133b81c09e5c855fc Mon Sep 17 00:00:00 2001 From: Emily Carden Date: Tue, 8 Aug 2017 10:37:32 -0400 Subject: [PATCH 34/34] no message --- app/components/recent-harvest.js | 4 --- app/templates/components/recent-harvest.hbs | 1 - .../components/recent-harvest-test.js | 25 ------------------- 3 files changed, 30 deletions(-) delete mode 100644 app/components/recent-harvest.js delete mode 100644 app/templates/components/recent-harvest.hbs delete mode 100644 tests/integration/components/recent-harvest-test.js diff --git a/app/components/recent-harvest.js b/app/components/recent-harvest.js deleted file mode 100644 index 926b61300..000000000 --- a/app/components/recent-harvest.js +++ /dev/null @@ -1,4 +0,0 @@ -import Ember from 'ember'; - -export default Ember.Component.extend({ -}); diff --git a/app/templates/components/recent-harvest.hbs b/app/templates/components/recent-harvest.hbs deleted file mode 100644 index 889d9eead..000000000 --- a/app/templates/components/recent-harvest.hbs +++ /dev/null @@ -1 +0,0 @@ -{{yield}} diff --git a/tests/integration/components/recent-harvest-test.js b/tests/integration/components/recent-harvest-test.js deleted file mode 100644 index 27367aacc..000000000 --- a/tests/integration/components/recent-harvest-test.js +++ /dev/null @@ -1,25 +0,0 @@ -import { moduleForComponent, test } from 'ember-qunit'; -import hbs from 'htmlbars-inline-precompile'; - -moduleForComponent('recent-harvest', 'Integration | Component | recent harvest', { - integration: true -}); - -test('it renders', function(assert) { - - // Set any properties with this.set('myProperty', 'value'); - // Handle any actions with this.on('myAction', function(val) { ... }); - - this.render(hbs`{{recent-harvest}}`); - - assert.equal(this.$().text().trim(), ''); - - // Template block usage: - this.render(hbs` - {{#recent-harvest}} - template block text - {{/recent-harvest}} - `); - - assert.equal(this.$().text().trim(), 'template block text'); -});