<commit_before>import asyncio
import datetime
import json
import os
import aiohttp
from aiohttp import web
from dateutil.parser import parse
from operator import itemgetter

import util
from fields import PointField, QueryField

SUNLIGHT_KEY = os.environ.get('SUNLIGHT_KEY')
SUNLIGHT_URL = 'https://congress.api.sunlightfoundation.com'

class Trigger(object):

    fields = None

    @property
    def fields(self):
        return {}

    def cache_key(self, request):
        key = request.path
        limit = request.data.get('limit')
        if limit is not None:
            key = '{}:limit={}'.format(key, limit)
        return key

    @asyncio.coroutine
    def check(self, fields, before, after, limit):
        raise NotImplemented()

    @asyncio.coroutine
    def get_json(self, url, params=None, headers=None, limit=None):

        if not params:
            params = {}

        if limit:
            params['per_page'] = limit

        if not headers:
            headers = {}
        headers.update({'X-APIKEY': SUNLIGHT_KEY})

        resp = yield from aiohttp.request(
            'get', url, params=params, headers=headers)
        data = yield from resp.json()
        return data

class CongressBirthdays(Trigger):

    @asyncio.coroutine
    def check(self, fields, before, after, limit):

        today = datetime.datetime.utcnow() - datetime.timedelta(hours=13)
        # today = today.replace(hour=0, minute=0, second=0)
        today = today.date()

        url = '{}/{}'.format(SUNLIGHT_URL, 'legislators')
        params = {
            'fields': ','.join(
                ["title", "first_name", "last_name", "state", "party",
                 "district", "birthday", "bioguide_id", "twitter_id"]),
            'per_page': 'all',
        }

        data = yield from self.get_json(url, params=params)

        legislators = []

        for legislator in data['results']:

            bday = parse(legislator['birthday'])
            bday = bday.replace(year=today.year, hour=0, minute=0, second=0)

            legislator['current_birthday'] = bday

            bday = bday.date()

            is_good = bday <= today

            if is_good and before:
                is_good = is_good and bday <= util.epoch_to_date(before)

            if is_good and after:
                is_good = is_good and bday > util.epoch_to_date(after)

            if is_good:
                legislators.append(legislator)

        legislators = sorted(legislators, key=itemgetter('last_name'))
        legislators = sorted(legislators,
                             key=itemgetter('current_birthday'),
                             reverse=True)

        ifttt = []

        for legislator in legislators:

            birth_year = parse(legislator['birthday']).year

            record = {
                'meta': {
                    'id': '{}/{}'.format(today.year, legislator['bioguide_id']),
                    'timestamp': int(legislator['current_birthday'].timestamp()),
                },
                'name': '{title}. {first_name} {last_name}'.format(**legislator),
                'state': '{state}-{district}'.format(**legislator) \
                    if legislator.get('district') else legislator['state'],
                'party': legislator['party'],
                'twitter_username': legislator.get('twitter_id') or '',
                'birthday_date': util.readable_date(legislator['birthday']),
                'numerical_birthday_date': legislator['birthday'],
                'birth_year': birth_year,
                'age': today.year - birth_year,
                'date': legislator['current_birthday'].date().isoformat(),
            }
            ifttt.append(record)

        return util.JSONResponse(ifttt[:limit or 20])

class NewBillsQuery(Trigger):

    fields = {
        'query': QueryField()
    }

    def cache_key(self, request):
        key = super(NewBillsQuery, self).cache_key(request)
        fields = request.data.get('triggerFields')
        if fields:
            query = fields.get('query')
            key = '{}:query={}'.format(key, query)
        return key

    @asyncio.coroutine
    def check(self, fields, before, after, limit):

        url = '{}/{}'.format(SUNLIGHT_URL, 'bills/search')
        params = {
            'fields': ','.join(
                ["bill_id", "bill_type", "number", "introduced_on",
                 "short_title", "official_title", "sponsor",
                 "urls.congress", "urls.opencongress"]),
            'query': fields.get('query'),
            'order': 'congress,introduced_on,number',
        }

        data = yield from self.get_json(url, params=params, limit=limit)

        if before:
            params['introduced_on__lte'] = util.epoch_to_date(before)
            params['order'] = 'introduced_on__desc'

        if after:
            params['introduced_on__gte'] = util.epoch_to_date(after)
            params['order'] = 'introduced_on__asc'

        ifttt = []

        for bill in data['results']:

            timestamp = util.date_to_epoch(bill['introduced_on'])

            record = {
                'meta': {
                    'id': bill['bill_id'],
                    'timestamp': timestamp,
                },
                'query': fields.get('query'),
                'sponsor_name': util.name(bill['sponsor']),
                'code': util.bill_code(bill),
                'title': util.bill_title(bill),
                'introduced_on': util.readable_date(bill['introduced_on']),
                'official_url': bill['urls']['congress'],
                'open_congress_url': bill['urls']['opencongress'],
                'date': bill['introduced_on'],
            }
            ifttt.append(record)

        return util.JSONResponse(ifttt)

class NewLawsTrigger(Trigger):

    @asyncio.coroutine
    def check(self, fields, before, after, limit):

        url = '{}/{}'.format(SUNLIGHT_URL, 'bills')
        params = {
            'fields': ','.join(
                ["bill_id", "bill_type", "number", "history.enacted_at",
                 "short_title", "official_title", "sponsor",
                 "urls.congress", "urls.opencongress"]),
            'history.enacted': 'true',
            'order': 'history.enacted_at',
        }

        if before:
            params['history.enacted_at__lte'] = util.epoch_to_date(before)
            params['order'] = 'history.enacted_at__desc'

        if after:
            params['history.enacted_at__gte'] = util.epoch_to_date(after)
            params['order'] = 'history.enacted_at__asc'

        data = yield from self.get_json(url, params=params, limit=limit)

        ifttt = []

        for bill in data['results']:

            timestamp = util.date_to_epoch(bill['history']['enacted_at'])

            record = {
                'meta': {
                    'id': bill['bill_id'],
                    'timestamp': timestamp,
                },
                'SponsorName': util.name(bill['sponsor']),
                'Code': util.bill_code(bill),
                'Title': util.bill_title(bill),
                'BecameLawOn': util.readable_date(bill['history']['enacted_at']),
                'OfficialURL': bill['urls']['congress'],
                'OpenCongressURL': bill['urls']['opencongress'],
                'date': bill['history']['enacted_at'],
            }
            ifttt.append(record)

        return util.JSONResponse(ifttt)

class NewLegislatorsTrigger(Trigger):

    fields = {
        'location': PointField()
    }

    def cache_key(self, request):
        key = super(NewLegislatorsTrigger, self).cache_key(request)
        fields = request.data.get('triggerFields')
        if fields:
            loc = fields.get('location')
            if loc:
                key = '{}:ll={},{}'.format(
                    key, loc['lat'], loc.get('lon') or loc.get('lng'))
        return key

    @asyncio.coroutine
    def check(self, fields, before, after, limit):

        limit = limit or 10

        loc = fields['location']

        url = '{}/{}'.format(SUNLIGHT_URL, 'legislators/locate')
        params = {
            'fields': ','.join(
                ["title", "first_name", "last_name", "bioguide_id",
                 "state", "party", "district", "terms",
                 "twitter_id", "phone", "website"]),
            'latitude': loc['lat'],
            'longitude': loc.get('lon') or loc.get('lng'),
        }

        data = yield from self.get_json(url, params=params)

        ifttt = []

        for legislator in data['results']:

            district = '{}{}'.format(
                legislator['state'], legislator.get('district') or '')

            terms = []
            for t in legislator['terms']:
                term_district = '{}{}'.format(
                    t['state'], t.get('district') or '')
                if term_district == district:
                    terms.append(t)

            term_start = terms[0]['start'];

            timestamp = util.time_to_epoch(term_start)

            record = {
                'meta': {
                    'id': '{}/{}'.format(legislator['bioguide_id'], district),
                    'timestamp': timestamp,
                },
                'name': '{title}. {first_name} {last_name}'.format(**legislator),
                'state': '{state}-{district}'.format(**legislator) \
                    if legislator.get('district') else legislator['state'],
                'party': legislator['party'],
                'phone': legislator.get('phone') or '',
                'website': legislator.get('website') or '',
                'twitter_username': legislator.get('twitter_id') or '',
                'date': term_start,
            }
            ifttt.append(record)

        ifttt = sorted(ifttt, key=lambda x: x['date'], reverse=True)

        return util.JSONResponse(ifttt[:limit])

class UpcomingBillsTrigger(Trigger):

    @asyncio.coroutine
    def check(self, fields, before, after, limit):

        url = '{}/{}'.format(SUNLIGHT_URL, 'upcoming_bills')
        params = {
            'fields': ','.join(
                ["bill_id", "chamber", "legislative_day",
                 "range", "url", "bill", "scheduled_at"]),
            'range__exists': 'true',
            'order': 'scheduled_at',
        }

        data = yield from self.get_json(url, params=params, limit=limit)

        ifttt = []

        for upcoming in data['results']:

            timestamp = util.time_to_epoch(upcoming['scheduled_at'])

            display_date = util.readable_date(upcoming['legislative_day'])
            if upcoming['range'] == 'week':
                display_date = "the week of " + display_date;

            bill = upcoming.get('bill')

            parts = util.parse_bill_id(upcoming['bill_id'])
            code = util.bill_code(parts) if parts else upcoming['bill_id'].strip()

            record = {
                'meta': {
                    'id': '{range}/{legislative_day}/{bill_id}'.format(**upcoming),
                    'timestamp': timestamp,
                },
                'Code': code,
                'Title': util.bill_title(bill) if bill else "(Not yet known)",
                'SponsorName': util.name(bill['sponsor']) if bill else "(Not yet known)",
                'LegislativeDate': display_date,
                'Chamber': util.chamber_name(upcoming['chamber']),
                'SourceURL': upcoming['url'],
                'date': upcoming['legislative_day'],
            }
            ifttt.append(record)

        return util.JSONResponse(ifttt)

congress_birthdays = CongressBirthdays()
new_bills_query = NewBillsQuery()
new_laws = NewLawsTrigger()
new_legislators = NewLegislatorsTrigger()
upcoming_bills = UpcomingBillsTrigger()
<commit_msg>use official URL instead of OpenCongress URL<commit_after>import asyncio
import datetime
import json
import os
import aiohttp
from aiohttp import web
from dateutil.parser import parse
from operator import itemgetter

import util
from fields import PointField, QueryField

SUNLIGHT_KEY = os.environ.get('SUNLIGHT_KEY')
SUNLIGHT_URL = 'https://congress.api.sunlightfoundation.com'

class Trigger(object):

    fields = None

    @property
    def fields(self):
        return {}

    def cache_key(self, request):
        key = request.path
        limit = request.data.get('limit')
        if limit is not None:
            key = '{}:limit={}'.format(key, limit)
        return key

    @asyncio.coroutine
    def check(self, fields, before, after, limit):
        raise NotImplemented()

    @asyncio.coroutine
    def get_json(self, url, params=None, headers=None, limit=None):

        if not params:
            params = {}

        if limit:
            params['per_page'] = limit

        if not headers:
            headers = {}
        headers.update({'X-APIKEY': SUNLIGHT_KEY})

        resp = yield from aiohttp.request(
            'get', url, params=params, headers=headers)
        data = yield from resp.json()
        return data

class CongressBirthdays(Trigger):

    @asyncio.coroutine
    def check(self, fields, before, after, limit):

        today = datetime.datetime.utcnow() - datetime.timedelta(hours=13)
        # today = today.replace(hour=0, minute=0, second=0)
        today = today.date()

        url = '{}/{}'.format(SUNLIGHT_URL, 'legislators')
        params = {
            'fields': ','.join(
                ["title", "first_name", "last_name", "state", "party",
                 "district", "birthday", "bioguide_id", "twitter_id"]),
            'per_page': 'all',
        }

        data = yield from self.get_json(url, params=params)

        legislators = []

        for legislator in data['results']:

            bday = parse(legislator['birthday'])
            bday = bday.replace(year=today.year, hour=0, minute=0, second=0)

            legislator['current_birthday'] = bday

            bday = bday.date()

            is_good = bday <= today

            if is_good and before:
                is_good = is_good and bday <= util.epoch_to_date(before)

            if is_good and after:
                is_good = is_good and bday > util.epoch_to_date(after)

            if is_good:
                legislators.append(legislator)

        legislators = sorted(legislators, key=itemgetter('last_name'))
        legislators = sorted(legislators,
                             key=itemgetter('current_birthday'),
                             reverse=True)

        ifttt = []

        for legislator in legislators:

            birth_year = parse(legislator['birthday']).year

            record = {
                'meta': {
                    'id': '{}/{}'.format(today.year, legislator['bioguide_id']),
                    'timestamp': int(legislator['current_birthday'].timestamp()),
                },
                'name': '{title}. {first_name} {last_name}'.format(**legislator),
                'state': '{state}-{district}'.format(**legislator) \
                    if legislator.get('district') else legislator['state'],
                'party': legislator['party'],
                'twitter_username': legislator.get('twitter_id') or '',
                'birthday_date': util.readable_date(legislator['birthday']),
                'numerical_birthday_date': legislator['birthday'],
                'birth_year': birth_year,
                'age': today.year - birth_year,
                'date': legislator['current_birthday'].date().isoformat(),
            }
            ifttt.append(record)

        return util.JSONResponse(ifttt[:limit or 20])

class NewBillsQuery(Trigger):

    fields = {
        'query': QueryField()
    }

    def cache_key(self, request):
        key = super(NewBillsQuery, self).cache_key(request)
        fields = request.data.get('triggerFields')
        if fields:
            query = fields.get('query')
            key = '{}:query={}'.format(key, query)
        return key

    @asyncio.coroutine
    def check(self, fields, before, after, limit):

        url = '{}/{}'.format(SUNLIGHT_URL, 'bills/search')
        params = {
            'fields': ','.join(
                ["bill_id", "bill_type", "number", "introduced_on",
                 "short_title", "official_title", "sponsor",
                 "urls.congress", "urls.opencongress"]),
            'query': fields.get('query'),
            'order': 'congress,introduced_on,number',
        }

        data = yield from self.get_json(url, params=params, limit=limit)

        if before:
            params['introduced_on__lte'] = util.epoch_to_date(before)
            params['order'] = 'introduced_on__desc'

        if after:
            params['introduced_on__gte'] = util.epoch_to_date(after)
            params['order'] = 'introduced_on__asc'

        ifttt = []

        for bill in data['results']:

            timestamp = util.date_to_epoch(bill['introduced_on'])

            record = {
                'meta': {
                    'id': bill['bill_id'],
                    'timestamp': timestamp,
                },
                'query': fields.get('query'),
                'sponsor_name': util.name(bill['sponsor']),
                'code': util.bill_code(bill),
                'title': util.bill_title(bill),
                'introduced_on': util.readable_date(bill['introduced_on']),
                'official_url': bill['urls']['congress'],
                'open_congress_url': bill['urls']['opencongress'],
                'date': bill['introduced_on'],
            }
            ifttt.append(record)

        return util.JSONResponse(ifttt)

class NewLawsTrigger(Trigger):

    @asyncio.coroutine
    def check(self, fields, before, after, limit):

        url = '{}/{}'.format(SUNLIGHT_URL, 'bills')
        params = {
            'fields': ','.join(
                ["bill_id", "bill_type", "number", "history.enacted_at",
                 "short_title", "official_title", "sponsor",
                 "urls.congress", "urls.opencongress"]),
            'history.enacted': 'true',
            'order': 'history.enacted_at',
        }

        if before:
            params['history.enacted_at__lte'] = util.epoch_to_date(before)
            params['order'] = 'history.enacted_at__desc'

        if after:
            params['history.enacted_at__gte'] = util.epoch_to_date(after)
            params['order'] = 'history.enacted_at__asc'

        data = yield from self.get_json(url, params=params, limit=limit)

        ifttt = []

        for bill in data['results']:

            timestamp = util.date_to_epoch(bill['history']['enacted_at'])

            record = {
                'meta': {
                    'id': bill['bill_id'],
                    'timestamp': timestamp,
                },
                'SponsorName': util.name(bill['sponsor']),
                'Code': util.bill_code(bill),
                'Title': util.bill_title(bill),
                'BecameLawOn': util.readable_date(bill['history']['enacted_at']),
                'OfficialURL': bill['urls']['congress'],
                'OpenCongressURL': bill['urls']['congress'],
                'date': bill['history']['enacted_at'],
            }
            ifttt.append(record)

        return util.JSONResponse(ifttt)

class NewLegislatorsTrigger(Trigger):

    fields = {
        'location': PointField()
    }

    def cache_key(self, request):
        key = super(NewLegislatorsTrigger, self).cache_key(request)
        fields = request.data.get('triggerFields')
        if fields:
            loc = fields.get('location')
            if loc:
                key = '{}:ll={},{}'.format(
                    key, loc['lat'], loc.get('lon') or loc.get('lng'))
        return key

    @asyncio.coroutine
    def check(self, fields, before, after, limit):

        limit = limit or 10

        loc = fields['location']

        url = '{}/{}'.format(SUNLIGHT_URL, 'legislators/locate')
        params = {
            'fields': ','.join(
                ["title", "first_name", "last_name", "bioguide_id",
                 "state", "party", "district", "terms",
                 "twitter_id", "phone", "website"]),
            'latitude': loc['lat'],
            'longitude': loc.get('lon') or loc.get('lng'),
        }

        data = yield from self.get_json(url, params=params)

        ifttt = []

        for legislator in data['results']:

            district = '{}{}'.format(
                legislator['state'], legislator.get('district') or '')

            terms = []
            for t in legislator['terms']:
                term_district = '{}{}'.format(
                    t['state'], t.get('district') or '')
                if term_district == district:
                    terms.append(t)

            term_start = terms[0]['start'];

            timestamp = util.time_to_epoch(term_start)

            record = {
                'meta': {
                    'id': '{}/{}'.format(legislator['bioguide_id'], district),
                    'timestamp': timestamp,
                },
                'name': '{title}. {first_name} {last_name}'.format(**legislator),
                'state': '{state}-{district}'.format(**legislator) \
                    if legislator.get('district') else legislator['state'],
                'party': legislator['party'],
                'phone': legislator.get('phone') or '',
                'website': legislator.get('website') or '',
                'twitter_username': legislator.get('twitter_id') or '',
                'date': term_start,
            }
            ifttt.append(record)

        ifttt = sorted(ifttt, key=lambda x: x['date'], reverse=True)

        return util.JSONResponse(ifttt[:limit])

class UpcomingBillsTrigger(Trigger):

    @asyncio.coroutine
    def check(self, fields, before, after, limit):

        url = '{}/{}'.format(SUNLIGHT_URL, 'upcoming_bills')
        params = {
            'fields': ','.join(
                ["bill_id", "chamber", "legislative_day",
                 "range", "url", "bill", "scheduled_at"]),
            'range__exists': 'true',
            'order': 'scheduled_at',
        }

        data = yield from self.get_json(url, params=params, limit=limit)

        ifttt = []

        for upcoming in data['results']:

            timestamp = util.time_to_epoch(upcoming['scheduled_at'])

            display_date = util.readable_date(upcoming['legislative_day'])
            if upcoming['range'] == 'week':
                display_date = "the week of " + display_date;

            bill = upcoming.get('bill')

            parts = util.parse_bill_id(upcoming['bill_id'])
            code = util.bill_code(parts) if parts else upcoming['bill_id'].strip()

            record = {
                'meta': {
                    'id': '{range}/{legislative_day}/{bill_id}'.format(**upcoming),
                    'timestamp': timestamp,
                },
                'Code': code,
                'Title': util.bill_title(bill) if bill else "(Not yet known)",
                'SponsorName': util.name(bill['sponsor']) if bill else "(Not yet known)",
                'LegislativeDate': display_date,
                'Chamber': util.chamber_name(upcoming['chamber']),
                'SourceURL': upcoming['url'],
                'date': upcoming['legislative_day'],
            }
            ifttt.append(record)

        return util.JSONResponse(ifttt)

congress_birthdays = CongressBirthdays()
new_bills_query = NewBillsQuery()
new_laws = NewLawsTrigger()
new_legislators = NewLegislatorsTrigger()
upcoming_bills = UpcomingBillsTrigger()
<|endoftext|>


<commit_before>"""
The Axis family of classes are used to represent a named axis within a
coordinate system. Axes can be regular discrete or continuous, finite or infinite.

The current class hirachy looks like this.

                 Axis
                   |
        ---------------------------
        |                         |
   RegularAxis             ContinuousAxis
        |
   VoxelAxis

There is currently no support for irregularly spaced axes, however this
could easily be added.
"""

import numpy as N

valid = ('time', 'xspace', 'yspace', 'zspace', 'vector_dimension', 'concat')
space = ('zspace', 'yspace', 'xspace')
spacetime = ('time', 'zspace', 'yspace', 'xspace')

class Axis(object):
    """
    This class represents a generic axis. Axes are used in the definition
    of CoordinateSystem.
    """

    def __init__(self, name):
        self.name = name
        if self.name not in valid:
            raise ValueError, ('%s is invalid: recognized dimension names are ' + `valid`) \
                % (self.name)

    def __eq__(self, other):
        """ Equality is defined by name """
        return self.name == other.name

    def valid(self, x):
        """ test if x is a point on the axis. """
        raise NotImplementedError

    def max(self):
        """ The maximum value of the axis. """
        raise NotImplementedError

    def min(self):
        """ The minimum value of the axis. """
        raise NotImplementedError

    def range(self):
        """ A (min, max) pair representing the range of the axis. """
        return (self.min(), self.max())

class ContinuousAxis(Axis):
    """
    This class represents an axis which is continuous on some range.
    This range can extend to infinity in either direction.
    """
    def __init__(self, name, low=-N.inf, high=N.inf):
        self.low = low
        self.high = high
        Axis.__init__(self, name)

    def __eq__(self, other):
        """ Equality is defined by name and range. """
        return self.range() == other.range() and \
               Axis.__eq__(self, other)

    def valid(self, x):
        """ The axis is defined as the range [low:high) """
        return self.low <= x < self.high

    def max(self):
        return self.high

    def min(self):
        return self.low

class RegularAxis (Axis):
    """
    This class represents a regularly spaced axis. Axes are used in the
    definition of Coordinate systems.

    >>> from neuroimaging.core.reference.axis import RegularAxis
    >>> from numpy import allclose, array
    >>> r = RegularAxis(name='xspace',length=10, start=0.25, step=0.3)
    >>> allclose(r.values(), array([ 0.25,  0.55,  0.85,  1.15,  1.45,  1.75,  2.05,  2.35,  2.65,  2.95]))
    True
    >>>
    """

    def __init__(self, name, length=N.inf, start=0, step=1):
        self.length = length
        self.start = start
        self.step = step
        Axis.__init__(self, name)

    def __eq__(self, other):
        """ Equality is defined by (start, stop, length) and name. """
        return self.length == other.length and \
               self.start == other.start and \
               self.step == other.step and \
               Axis.__eq__(self, other)

    def valid(self, x):
        if x in (-N.inf, N.inf):
            return False
        if self.length == N.inf:
            tmp = (x - self.start)/self.step
            return (tmp == int(tmp)) and (0 <= tmp < self.length)
        else:
            return x in self.values()

    def values(self):
        """
        Return all the values in the axis.
        Return a generator for the infinite case
        """
        if self.length == N.inf:
            def f(x):
                while True:
                    yield x
                    x += self.step
            return f(self.start)
        else:
            return N.linspace(self.min(), self.max() + self.step, self.length, False)

    def max(self):
        return self.start + self.step*(self.length - 1)

    def min(self):
        return self.start

class VoxelAxis (RegularAxis):
    """
    A RegularAxis which starts at 0 and has a step of 1.
    """
    def __init__(self, name, length=N.inf):
        RegularAxis.__init__(self, name, length, start=0, step=1)

generic = (
  ContinuousAxis(name='zspace'),
  ContinuousAxis(name='yspace'),
  ContinuousAxis(name='xspace'))
"""Default axes"""

<commit_msg>add docs<commit_after>"""
The Axis family of classes are used to represent a named axis within a
coordinate system. Axes can be regular discrete or continuous, finite or infinite.

The current class hirachy looks like this.

                 Axis
                   |
        ---------------------------
        |                         |
   RegularAxis             ContinuousAxis
        |
   VoxelAxis

There is currently no support for irregularly spaced axes, however this
could easily be added.
"""

import numpy as N

valid = ('time', 'xspace', 'yspace', 'zspace', 'vector_dimension', 'concat')
space = ('zspace', 'yspace', 'xspace')
spacetime = ('time', 'zspace', 'yspace', 'xspace')

class Axis(object):
    """
    This class represents a generic axis. Axes are used in the definition
    of CoordinateSystem.
    """

    def __init__(self, name):
        """
        Create an axis with a given C{name}.

        @param name: The name for the axis
        @type name: C{string}
        @precondition: C{name} must be an element of L{axis.valid}
        """

        self.name = name
        if self.name not in valid:
            raise ValueError, ('%s is invalid: recognized dimension names are ' + `valid`) \
                % (self.name)

    def __eq__(self, other):
        """ Equality is defined by name """
        return self.name == other.name

    def valid(self, x):
        """ test if x is a point on the axis. """
        raise NotImplementedError

    def max(self):
        """ The maximum value of the axis. """
        raise NotImplementedError

    def min(self):
        """ The minimum value of the axis. """
        raise NotImplementedError

    def range(self):
        """ A (min, max) pair representing the range of the axis. """
        return (self.min(), self.max())

class ContinuousAxis(Axis):
    """
    This class represents an axis which is continuous on some range.
    This range can extend to infinity in either direction.
    """
    def __init__(self, name, low=-N.inf, high=N.inf):
        self.low = low
        self.high = high
        Axis.__init__(self, name)

    def __eq__(self, other):
        """ Equality is defined by name and range. """
        return self.range() == other.range() and \
               Axis.__eq__(self, other)

    def valid(self, x):
        """ The axis is defined as the range [low:high) """
        return self.low <= x < self.high

    def max(self):
        return self.high

    def min(self):
        return self.low

class RegularAxis (Axis):
    """
    This class represents a regularly spaced axis. Axes are used in the
    definition of Coordinate systems.

    >>> from neuroimaging.core.reference.axis import RegularAxis
    >>> from numpy import allclose, array
    >>> r = RegularAxis(name='xspace',length=10, start=0.25, step=0.3)
    >>> allclose(r.values(), array([ 0.25,  0.55,  0.85,  1.15,  1.45,  1.75,  2.05,  2.35,  2.65,  2.95]))
    True
    >>>
    """

    def __init__(self, name, length=N.inf, start=0, step=1):
        self.length = length
        self.start = start
        self.step = step
        Axis.__init__(self, name)

    def __eq__(self, other):
        """ Equality is defined by (start, stop, length) and name. """
        return self.length == other.length and \
               self.start == other.start and \
               self.step == other.step and \
               Axis.__eq__(self, other)

    def valid(self, x):
        if x in (-N.inf, N.inf):
            return False
        if self.length == N.inf:
            tmp = (x - self.start)/self.step
            return (tmp == int(tmp)) and (0 <= tmp < self.length)
        else:
            return x in self.values()

    def values(self):
        """
        Return all the values in the axis.
        Return a generator for the infinite case
        """
        if self.length == N.inf:
            def f(x):
                while True:
                    yield x
                    x += self.step
            return f(self.start)
        else:
            return N.linspace(self.min(), self.max() + self.step, self.length, False)

    def max(self):
        return self.start + self.step*(self.length - 1)

    def min(self):
        return self.start

class VoxelAxis (RegularAxis):
    """
    A RegularAxis which starts at 0 and has a step of 1.
    """
    def __init__(self, name, length=N.inf):
        RegularAxis.__init__(self, name, length, start=0, step=1)

generic = (
  ContinuousAxis(name='zspace'),
  ContinuousAxis(name='yspace'),
  ContinuousAxis(name='xspace'))
"""Default axes"""

<|endoftext|>


<commit_before>#!/usr/bin/env python
# Copyright (C) 2014, 2015 by Ali Baharev <ali.baharev@gmail.com>
# All rights reserved.
# BSD license.
from __future__ import print_function
import subprocess
import sys
from os import access, listdir, makedirs, remove, X_OK
from os.path import dirname, isdir, isfile, join, normcase, normpath
import platform
import shutil

# A hackish way to import the configuration
sys.path.append(dirname(__file__))
from configuration import ETALON_DIR, TOCOMP_DIR, SG2PS_HOME, EXTENSION
from csv_test import main as csvtest_main

#===============================================================================

WIN = platform.system()=='Windows'

# Assumes the default directory layout of Eclipse and project name SG2PS
#SG2PS_EXE = join(SG2PS_HOME, 'Debug', 'SG2PS.exe' if WIN else 'SG2PS')
SG2PS_EXE = '/home/ali/ws-pydev/CSV_Test/sg2ps'
FLAG =  '--debug'
INPUT_EXT = '.rgf'
RGF_FOLDER = '/home/ali/sg2ps_tests/rgf_folder'
#RGF_FOLDER = '/home/ali/sg2ps_tests/empty'
# Check configuration.py too!

#===============================================================================

def main():

    # Check whether we need to invoke CSV test after generating the input
    NO_TEST = '--notest'
    argc = len(sys.argv)
    if argc > 2 or (argc==2 and sys.argv[1] != NO_TEST):
        print('Only the optional', NO_TEST, 'argument is supported')
        return
    RUN_CSVTEST = argc < 2

    if is_there_path_error():
        print('Exiting...')
        return
    print('All paths seem sane')

    # Delete the TOCOMP_DIR as it may contain files from a previous run
    if isdir(TOCOMP_DIR):
        shutil.rmtree(TOCOMP_DIR)
    makedirs(TOCOMP_DIR)

    # Copy the input files from the RGF folder to the test directory TOCOMP_DIR
    to_cp = sorted(f for f in listdir(RGF_FOLDER) if isfile(join(RGF_FOLDER,f)))
    for f in to_cp:
        shutil.copy(join(RGF_FOLDER, f), TOCOMP_DIR)
    print('Copied', len(to_cp), 'files')

    # Run the sg2ps executable on the projects in TOCOMP_DIR
    projects = [f[:-len(INPUT_EXT)] for f in to_cp if f.endswith(INPUT_EXT)]
    if not projects:
        print('Something is wrong, no projects found...')
        return
    #
    for f in projects:
        print('Processing:', f)
        ret = subprocess.call([SG2PS_EXE, FLAG, f], cwd=TOCOMP_DIR)
        if ret:
            print('Fatal error when calling {}, exiting...'.format(SG2PS_EXE))
            return
    print('Test file generation finished')

    # Keep only the result files
    allcontent = [ join(TOCOMP_DIR, f) for f in listdir(TOCOMP_DIR) ]
    keep = { f for f in allcontent if isfile(f) and f.endswith(EXTENSION) }
    for f in allcontent:
        if f in keep:
            continue
        elif isfile(f):
            remove(f)
        else:
            shutil.rmtree(f)

    # Each project should generate exactly one CSV file
    if len(keep) != len(projects):
        print('Something is strange:',len(projects),'projects but ',end='')
        print(len(keep),'CSV files, exiting...')
        return

    if RUN_CSVTEST:
        print('Invoking CSV test now\n')
        csvtest_main()
    else:
        print('Not running tests as requested; we are done!')

def is_there_path_error():
    # Consider replacing this long if - elif with a loop
    if not isfile(SG2PS_EXE) or not access(SG2PS_EXE, X_OK):
        print('SG2PS is not executable, check: "{}"'.format(SG2PS_EXE))
    elif not isdir(ETALON_DIR):
        print('ETALON_DIR: not a valid directory path "{}"'.format(ETALON_DIR))
    elif not isdir(RGF_FOLDER):
        print('RGF_FOLDER: not a valid directory path "{}"'.format(RGF_FOLDER))
    elif not isdir(TOCOMP_DIR):
        print('TOCOMP_DIR "{}" will be created'.format(TOCOMP_DIR))
        return False
    # TOCOMP_DIR exists and will be deleted: Check if that can cause data loss
    elif samefile_or_dir(TOCOMP_DIR, ETALON_DIR):
        print('Etalon and test directory are the same "{}"'.format(ETALON_DIR))
    elif samefile_or_dir(TOCOMP_DIR, RGF_FOLDER):
        print('RGF and test directory are the same: "{}"'.format(RGF_FOLDER))
    elif samefile_or_dir(TOCOMP_DIR, dirname(TOCOMP_DIR)):
        print('Give a non-root TOCOMP_DIR directory: "{}"'.format(TOCOMP_DIR))
    else:
        return False
    return True

def samefile_or_dir(f1, f2):
    try:
	    from os.path import samefile
    except ImportError:
        return normcase(normpath(f1)) == normcase(normpath(f2))
    return samefile(f1, f2)

if __name__=='__main__':
    main()
<commit_msg>Fixed a bogus import; fixed an indentation error<commit_after>#!/usr/bin/env python
# Copyright (C) 2014, 2015 by Ali Baharev <ali.baharev@gmail.com>
# All rights reserved.
# BSD license.
from __future__ import print_function
import subprocess
import sys
from os import access, listdir, makedirs, remove, X_OK
from os.path import dirname, isdir, isfile, join, normcase, normpath
import platform
import shutil

# A hackish way to import the configuration
sys.path.append(dirname(__file__))
from configuration import ETALON_DIR, TOCOMP_DIR, EXTENSION
from csv_test import main as csvtest_main

#===============================================================================

WIN = platform.system()=='Windows'

# Assumes the default directory layout of Eclipse and project name SG2PS
# SG2PS_HOME is defined in configuration
#SG2PS_EXE = join(SG2PS_HOME, 'Debug', 'SG2PS.exe' if WIN else 'SG2PS')
SG2PS_EXE = '/home/ali/ws-pydev/CSV_Test/sg2ps'
FLAG =  '--debug'
INPUT_EXT = '.rgf'
RGF_FOLDER = '/home/ali/sg2ps_tests/rgf_folder'
#RGF_FOLDER = '/home/ali/sg2ps_tests/empty'
# Check configuration.py too!

#===============================================================================

def main():

    # Check whether we need to invoke CSV test after generating the input
    NO_TEST = '--notest'
    argc = len(sys.argv)
    if argc > 2 or (argc==2 and sys.argv[1] != NO_TEST):
        print('Only the optional', NO_TEST, 'argument is supported')
        return
    RUN_CSVTEST = argc < 2

    if is_there_path_error():
        print('Exiting...')
        return
    print('All paths seem sane')

    # Delete the TOCOMP_DIR as it may contain files from a previous run
    if isdir(TOCOMP_DIR):
        shutil.rmtree(TOCOMP_DIR)
    makedirs(TOCOMP_DIR)

    # Copy the input files from the RGF folder to the test directory TOCOMP_DIR
    to_cp = sorted(f for f in listdir(RGF_FOLDER) if isfile(join(RGF_FOLDER,f)))
    for f in to_cp:
        shutil.copy(join(RGF_FOLDER, f), TOCOMP_DIR)
    print('Copied', len(to_cp), 'files')

    # Run the sg2ps executable on the projects in TOCOMP_DIR
    projects = [f[:-len(INPUT_EXT)] for f in to_cp if f.endswith(INPUT_EXT)]
    if not projects:
        print('Something is wrong, no projects found...')
        return
    #
    for f in projects:
        print('Processing:', f)
        ret = subprocess.call([SG2PS_EXE, FLAG, f], cwd=TOCOMP_DIR)
        if ret:
            print('Fatal error when calling {}, exiting...'.format(SG2PS_EXE))
            return
    print('Test file generation finished')

    # Keep only the result files
    allcontent = [ join(TOCOMP_DIR, f) for f in listdir(TOCOMP_DIR) ]
    keep = { f for f in allcontent if isfile(f) and f.endswith(EXTENSION) }
    for f in allcontent:
        if f in keep:
            continue
        elif isfile(f):
            remove(f)
        else:
            shutil.rmtree(f)

    # Each project should generate exactly one CSV file
    if len(keep) != len(projects):
        print('Something is strange:',len(projects),'projects but ',end='')
        print(len(keep),'CSV files, exiting...')
        return

    if RUN_CSVTEST:
        print('Invoking CSV test now\n')
        csvtest_main()
    else:
        print('Not running tests as requested; we are done!')

def is_there_path_error():
    # Consider replacing this long if - elif with a loop
    if not isfile(SG2PS_EXE) or not access(SG2PS_EXE, X_OK):
        print('SG2PS is not executable, check: "{}"'.format(SG2PS_EXE))
    elif not isdir(ETALON_DIR):
        print('ETALON_DIR: not a valid directory path "{}"'.format(ETALON_DIR))
    elif not isdir(RGF_FOLDER):
        print('RGF_FOLDER: not a valid directory path "{}"'.format(RGF_FOLDER))
    elif not isdir(TOCOMP_DIR):
        print('TOCOMP_DIR "{}" will be created'.format(TOCOMP_DIR))
        return False
    # TOCOMP_DIR exists and will be deleted: Check if that can cause data loss
    elif samefile_or_dir(TOCOMP_DIR, ETALON_DIR):
        print('Etalon and test directory are the same "{}"'.format(ETALON_DIR))
    elif samefile_or_dir(TOCOMP_DIR, RGF_FOLDER):
        print('RGF and test directory are the same: "{}"'.format(RGF_FOLDER))
    elif samefile_or_dir(TOCOMP_DIR, dirname(TOCOMP_DIR)):
        print('Give a non-root TOCOMP_DIR directory: "{}"'.format(TOCOMP_DIR))
    else:
        return False
    return True

def samefile_or_dir(f1, f2):
    try:
        from os.path import samefile
    except ImportError:
        return normcase(normpath(f1)) == normcase(normpath(f2))
    return samefile(f1, f2)

if __name__=='__main__':
    main()
<|endoftext|>


<commit_before>#The MIT License (MIT)
#
#Copyright (c) 2013 David Ryack
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of
#this software and associated documentation files (the "Software"), to deal in
#the Software without restriction, including without limitation the rights to
#use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
#the Software, and to permit persons to whom the Software is furnished to do so,
#subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all
#copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
#FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
#COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
#IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
#CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

# Hamurabi in Python

import random
import os
import sys
import inspect

#since all player input in the game should either be a \r or a positive number
#this function wraps raw_input and only returns a value if the input
#meets the above criteria.
#otherwise it will print a brief message and call itself again
def get_input(prompt):

        framec = inspect.stack()[1][3]
        print '##################################################'
        try:
                #print framev.f_locals['sumer'].bushels
                print framec
        except:
                print 'Ooops!'
        print '##################################################'
        input = raw_input(prompt)
        if ((input.isdigit() == True) and (int(input) >= 0)) or (input == ""):
                return input
        else:
                print "Try again Hamurabi!"
                return get_input(prompt)

def test_get_input(prompt):
        print prompt

        # peeking at sumer method which has called this func
        frame_c = inspect.stack()[1][3]

        # peeking at source code context -
        # in order to use prompt for selling acres
        frame_s = inspect.stack()[1][4]

        if frame_c == 'main':
            return str(100)     #request 100 turn game
        else:
            # peeking at variables within sumer
            frame_v = inspect.stack()[2][0]
            bush = frame_v.f_locals['sumer'].bushels
            pop = frame_v.f_locals['sumer'].population
            acre = frame_v.f_locals['sumer'].acres

            if frame_c == 'get_acres':
                #check and see if we're selling
                if frame_s == '["                        inp = get_input(\'How many acres do you wish to sell => \')\n"]':
                    if bush <= (pop * 11) + acre:
                        return str(acre / 7)
                    if bush <= (pop * 8) + acre:
                        return str(acre / 5)
                    elif (acre / pop) > 15:
                        return str(acre / 10)
                    else:
                        return str(0)
                else:       # we're buying
                    if bush >= (pop * 30) + acre:
                        return str(random.randint(50, 200))
                    elif bush >= (pop * 20) + acre:
                        return str(random.randint(25,50))
                    elif bush >= (pop * 15) + acre:
                        if (acre / pop) < 8:
                            return str(random.randint(1, 25))
                        else:
                            return str(0)
                    else:
                        return str(0)
            elif frame_c == 'feed_people':
                max_feed = bush - acre      #set aside bushels for planting
                if bush >= acre:
                    if max_feed >= 40 * pop:     #if bumper crop, feed almost everyone
                        return str(18 * pop)
                    elif max_feed >= (15 * pop):
                        return str(13 * pop)
                    else:
                        return str(max_feed)

                else:
                    return str(max_feed + (bush / random.randint(5,15)))

            elif frame_c == 'plant_fields':
                max_plant = 10 * pop
                if bush >= max_plant and bush >= acre:
                    if acre > max_plant:
                        return str(max_plant)
                    else:
                        return str(acre)
                elif bush >= max_plant and bush <= acre:
                    return str(max_plant)
                elif bush <= max_plant and bush >= acre:
                    return str(acre)
                else:
                    return str(bush)

            else:
                print "REACHED UNEXPECTED STACK FRAME: %s" % frame_c

class CityState():
        def __init__(self, turns):
                self.turns_tp = turns
                self.year = 0
                self.population = 100
                self.starved = 0
                self.migrated = 0
                self.bushels = 2800
                self.acres = 1000
                self.byield = 3
                self.pests = 200
                self.tradeval =  0
                self.avg_starved = 0
                self.totaldied = 0
                self.fed = 100
                self.planted = 0
                self.died = 0
                self.print_year_end()

        def check_for_plague(self):
                if (self.year > 0) and (random.randint(0,15) == 0):  #check for plague
                        print "A horrible plague has struck!  Many have died!"
                        self.died = self.population/(random.randint(0,4)+2) #calculate losses
                        self.population -= self.died
                        self.totaldied += self.died

        def print_year_end(self):
                if self.year > 0:
                        self.do_numbers()

                #set this year's tradevalue
                self.tradeval = 17+random.randint(0, 10)
                print "\nMy lord in the year " + str(self.year) + " I beg to report to you that"
                print str(self.starved) + " people starved, and " + str(self.migrated) + " came to the city."
                self.check_for_plague()
                print "Population is now " + str(self.population) + "."
                print "The city now owns " + str(self.acres) + " acres of land."
                print "You have harvested " + str(self.byield) + " bushels per acre."
                print "Rats ate " + str(self.pests) + " bushels of grain."
                print "We now have " + str(self.bushels) + " in store."
                print "Land is trading at " + str(self.tradeval) + " bushels per acre.\n"
                self.year += 1

        def get_acres(self):
                get_input =test_get_input

                inp = get_input('How many acres do you wish to buy => ')
                if (inp == "") or (int(inp) == 0):      #player doesn't want to buy
                        inp = get_input('How many acres do you wish to sell => ')
                        if (inp == "") or (int(inp) == 0):
                                return True     #neither buying nor selling
                        else:
                                if int(inp) > self.acres:
                                        print "Think again Hamurabi, you only have " + str(self.acres) + " acres to sell!"
                                        return False  #tried to sell more than currently owned
                                else:
                                        self.acres -= int(inp)       #reduce acres by number sold
                                        self.bushels += int(inp)*self.tradeval    #increase bushels based on number acres sold
                                        return True
                else:
                        if int(inp)*self.tradeval > self.bushels:
                                print "Think again Hamurabi, you only have " + str(self.bushels) + " bushels to use for purchase!"
                                return False    #tried to buy more than can be afforded
                        else:
                                self.acres += int(inp)       #increase acres by num purchased
                                self.bushels -= int(inp)*self.tradeval    #reduce bushels based on purchased acres
                                return True

        def feed_people(self):
                get_input =test_get_input

                print "This year you will require " + str(self.population*20) + " bushels to avoid starving anyone."
                inp = get_input('How many bushels do you wish to release to your people? => ')
                if inp == "":
                        return False #player didn't enter anything
                else:
                        if int(inp) > self.bushels:
                                print "Think again Hamurabi, you only have " + str(self.bushels) + " available!"
                                return False
                        else:
                                self.bushels -= int(inp)
                                self.fed = int(inp)/20
                                return True

        def plant_fields(self):
                get_input =test_get_input

                inp = get_input('How many fields will you plant => ')
                if inp == "":
                        return False    #player didn't enter anything
                else:
                        if int(inp) > self.bushels:
                                print "Think again Hamurabi, you only have " + str(self.bushels) + " bushels available!"
                                return False
                        elif int(inp) > self.population * 10: # people can only plant 10 acres each
                                print "Think again Hamurabi, you only have " + str(self.population) + " people to plant the fields!"
                                return False
                        elif int(inp) > self.acres:
                                print "Think again Hamurabi, you only have " + str(self.acres) + " acres to plant!"
                                return False
                        else:
                                self.bushels -= int(inp)
                                self.planted = int(inp)
                                return True

        def check_for_overthrow(self):
                if self.starved > int(0.45 * self.population):
                        print "\nYou starved starved " + str(self.starved) + " out of a population of only " + str(self.population) + ","
                        print "this has caused you to be deposed by force!\n"
                        self.totaldied += self.starved
                        self.print_end_reign()
                        sys.exit(0)   #end game due to starvation

        def do_numbers(self):
                self.byield = random.randint(1, 10)     #set bushel yield per acre for the year

                self.starved = self.population - self.fed
                if self.starved < 0:
                        self.starved = 0
                self.avg_starved += int((float(self.starved)/float(self.population))*100)
                self.births = int(self.population / random.randint(2, 10))
                self.population += self.births
                self.check_for_overthrow()
                self.avg_starved += int((float(self.starved)/float(self.population))*100)
                self.population -= self.starved #children can die
                self.migrated =  int(0.1 * random.randint(1, self.population))
                self.population += self.migrated #but immigrants don't

                self.pests = int(self.bushels / random.randint(1, 5)+2)
                self.bushels += self.planted * self.byield
                self.bushels -= self.pests
                if self.bushels < 0:
                        self.bushels = 0

                self.totaldied += self.starved

        def print_end_reign(self):
                print "In your " + str(self.year) + " year term of office " + str(int(self.avg_starved/self.year)) + " percent of"
                print "population starved per year on average.  A total"
                print "of " + str(self.totaldied) + " people died during your term."
                print "The city began with 10 acres per person and ended with"
                print str(self.acres/self.population) + " acres per person."

                if int(self.avg_starved / self.year) > 33:
                        print "Due to this extreme mismanagement you have not only"
                        print "been impeached and thrown out of office, but you have"
                        print "also been declared 'National Fink'!!\n"

                elif int(self.avg_starved / self.year) > 10:
                        print "Your heavy handed performance smacks of Nero and Ivan IV."
                        print "The people remaining find you an unpleasant ruler, and"
                        print "frankly, hate your guts!\n"
                else:
                        print "Your performance could have been somewhat better, but"
                        print "really wasn't too bad at all."
                        print str(random.randint(1, self.population)) + " people would dearly like to see you assassinated,"
                        print "but we all have our trivial problems.\n"

                print "<<-----------<END>----------->>"

def main():
        os.system('clear')      #clear screen

        get_input = test_get_input

        gameturns = get_input('How many years would you like to play? => ')
        if (gameturns == '') or (int(gameturns) <= 0):
                gameturns = 10
        sumer = CityState(int(gameturns))

        while int(gameturns) > 0:

                tmp = False
                while tmp == False:
                        tmp = sumer.get_acres()

                tmp = False
                while tmp == False:
                        tmp = sumer.feed_people()

                tmp = False
                while tmp == False:
                        tmp = sumer.plant_fields()

                gameturns = int(gameturns) - 1
                sumer.print_year_end()

        sumer.print_end_reign()

if __name__ == "__main__":
        main()
<commit_msg>Deleted some testing code accidentally remaining.<commit_after>#The MIT License (MIT)
#
#Copyright (c) 2013 David Ryack
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of
#this software and associated documentation files (the "Software"), to deal in
#the Software without restriction, including without limitation the rights to
#use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
#the Software, and to permit persons to whom the Software is furnished to do so,
#subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all
#copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
#FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
#COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
#IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
#CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

# Hamurabi in Python

import random
import os
import sys
import inspect

#since all player input in the game should either be a \r or a positive number
#this function wraps raw_input and only returns a value if the input
#meets the above criteria.
#otherwise it will print a brief message and call itself again
def get_input(prompt):
        input = raw_input(prompt)
        if ((input.isdigit() == True) and (int(input) >= 0)) or (input == ""):
                return input
        else:
                print "Try again Hamurabi!"
                return get_input(prompt)

def test_get_input(prompt):
        print prompt

        # peeking at sumer method which has called this func
        frame_c = inspect.stack()[1][3]

        # peeking at source code context -
        # in order to use prompt for selling acres
        frame_s = inspect.stack()[1][4]

        if frame_c == 'main':
            return str(100)     #request 100 turn game
        else:
            # peeking at variables within sumer
            frame_v = inspect.stack()[2][0]
            bush = frame_v.f_locals['sumer'].bushels
            pop = frame_v.f_locals['sumer'].population
            acre = frame_v.f_locals['sumer'].acres

            if frame_c == 'get_acres':
                #check and see if we're selling
                if frame_s == '["                        inp = get_input(\'How many acres do you wish to sell => \')\n"]':
                    if bush <= (pop * 11) + acre:
                        return str(acre / 7)
                    if bush <= (pop * 8) + acre:
                        return str(acre / 5)
                    elif (acre / pop) > 15:
                        return str(acre / 10)
                    else:
                        return str(0)
                else:       # we're buying
                    if bush >= (pop * 30) + acre:
                        return str(random.randint(50, 200))
                    elif bush >= (pop * 20) + acre:
                        return str(random.randint(25,50))
                    elif bush >= (pop * 15) + acre:
                        if (acre / pop) < 8:
                            return str(random.randint(1, 25))
                        else:
                            return str(0)
                    else:
                        return str(0)
            elif frame_c == 'feed_people':
                max_feed = bush - acre      #set aside bushels for planting
                if bush >= acre:
                    if max_feed >= 40 * pop:     #if bumper crop, feed almost everyone
                        return str(18 * pop)
                    elif max_feed >= (15 * pop):
                        return str(13 * pop)
                    else:
                        return str(max_feed)

                else:
                    return str(max_feed + (bush / random.randint(5,15)))

            elif frame_c == 'plant_fields':
                max_plant = 10 * pop
                if bush >= max_plant and bush >= acre:
                    if acre > max_plant:
                        return str(max_plant)
                    else:
                        return str(acre)
                elif bush >= max_plant and bush <= acre:
                    return str(max_plant)
                elif bush <= max_plant and bush >= acre:
                    return str(acre)
                else:
                    return str(bush)

            else:
                print "REACHED UNEXPECTED STACK FRAME: %s" % frame_c

class CityState():
        def __init__(self, turns):
                self.turns_tp = turns
                self.year = 0
                self.population = 100
                self.starved = 0
                self.migrated = 0
                self.bushels = 2800
                self.acres = 1000
                self.byield = 3
                self.pests = 200
                self.tradeval =  0
                self.avg_starved = 0
                self.totaldied = 0
                self.fed = 100
                self.planted = 0
                self.died = 0
                self.print_year_end()

        def check_for_plague(self):
                if (self.year > 0) and (random.randint(0,15) == 0):  #check for plague
                        print "A horrible plague has struck!  Many have died!"
                        self.died = self.population/(random.randint(0,4)+2) #calculate losses
                        self.population -= self.died
                        self.totaldied += self.died

        def print_year_end(self):
                if self.year > 0:
                        self.do_numbers()

                #set this year's tradevalue
                self.tradeval = 17+random.randint(0, 10)
                print "\nMy lord in the year " + str(self.year) + " I beg to report to you that"
                print str(self.starved) + " people starved, and " + str(self.migrated) + " came to the city."
                self.check_for_plague()
                print "Population is now " + str(self.population) + "."
                print "The city now owns " + str(self.acres) + " acres of land."
                print "You have harvested " + str(self.byield) + " bushels per acre."
                print "Rats ate " + str(self.pests) + " bushels of grain."
                print "We now have " + str(self.bushels) + " in store."
                print "Land is trading at " + str(self.tradeval) + " bushels per acre.\n"
                self.year += 1

        def get_acres(self):
                get_input =test_get_input

                inp = get_input('How many acres do you wish to buy => ')
                if (inp == "") or (int(inp) == 0):      #player doesn't want to buy
                        inp = get_input('How many acres do you wish to sell => ')
                        if (inp == "") or (int(inp) == 0):
                                return True     #neither buying nor selling
                        else:
                                if int(inp) > self.acres:
                                        print "Think again Hamurabi, you only have " + str(self.acres) + " acres to sell!"
                                        return False  #tried to sell more than currently owned
                                else:
                                        self.acres -= int(inp)       #reduce acres by number sold
                                        self.bushels += int(inp)*self.tradeval    #increase bushels based on number acres sold
                                        return True
                else:
                        if int(inp)*self.tradeval > self.bushels:
                                print "Think again Hamurabi, you only have " + str(self.bushels) + " bushels to use for purchase!"
                                return False    #tried to buy more than can be afforded
                        else:
                                self.acres += int(inp)       #increase acres by num purchased
                                self.bushels -= int(inp)*self.tradeval    #reduce bushels based on purchased acres
                                return True

        def feed_people(self):
                get_input =test_get_input

                print "This year you will require " + str(self.population*20) + " bushels to avoid starving anyone."
                inp = get_input('How many bushels do you wish to release to your people? => ')
                if inp == "":
                        return False #player didn't enter anything
                else:
                        if int(inp) > self.bushels:
                                print "Think again Hamurabi, you only have " + str(self.bushels) + " available!"
                                return False
                        else:
                                self.bushels -= int(inp)
                                self.fed = int(inp)/20
                                return True

        def plant_fields(self):
                get_input =test_get_input

                inp = get_input('How many fields will you plant => ')
                if inp == "":
                        return False    #player didn't enter anything
                else:
                        if int(inp) > self.bushels:
                                print "Think again Hamurabi, you only have " + str(self.bushels) + " bushels available!"
                                return False
                        elif int(inp) > self.population * 10: # people can only plant 10 acres each
                                print "Think again Hamurabi, you only have " + str(self.population) + " people to plant the fields!"
                                return False
                        elif int(inp) > self.acres:
                                print "Think again Hamurabi, you only have " + str(self.acres) + " acres to plant!"
                                return False
                        else:
                                self.bushels -= int(inp)
                                self.planted = int(inp)
                                return True

        def check_for_overthrow(self):
                if self.starved > int(0.45 * self.population):
                        print "\nYou starved starved " + str(self.starved) + " out of a population of only " + str(self.population) + ","
                        print "this has caused you to be deposed by force!\n"
                        self.totaldied += self.starved
                        self.print_end_reign()
                        sys.exit(0)   #end game due to starvation

        def do_numbers(self):
                self.byield = random.randint(1, 10)     #set bushel yield per acre for the year

                self.starved = self.population - self.fed
                if self.starved < 0:
                        self.starved = 0
                self.avg_starved += int((float(self.starved)/float(self.population))*100)
                self.births = int(self.population / random.randint(2, 10))
                self.population += self.births
                self.check_for_overthrow()
                self.avg_starved += int((float(self.starved)/float(self.population))*100)
                self.population -= self.starved #children can die
                self.migrated =  int(0.1 * random.randint(1, self.population))
                self.population += self.migrated #but immigrants don't

                self.pests = int(self.bushels / random.randint(1, 5)+2)
                self.bushels += self.planted * self.byield
                self.bushels -= self.pests
                if self.bushels < 0:
                        self.bushels = 0

                self.totaldied += self.starved

        def print_end_reign(self):
                print "In your " + str(self.year) + " year term of office " + str(int(self.avg_starved/self.year)) + " percent of"
                print "population starved per year on average.  A total"
                print "of " + str(self.totaldied) + " people died during your term."
                print "The city began with 10 acres per person and ended with"
                print str(self.acres/self.population) + " acres per person."

                if int(self.avg_starved / self.year) > 33:
                        print "Due to this extreme mismanagement you have not only"
                        print "been impeached and thrown out of office, but you have"
                        print "also been declared 'National Fink'!!\n"

                elif int(self.avg_starved / self.year) > 10:
                        print "Your heavy handed performance smacks of Nero and Ivan IV."
                        print "The people remaining find you an unpleasant ruler, and"
                        print "frankly, hate your guts!\n"
                else:
                        print "Your performance could have been somewhat better, but"
                        print "really wasn't too bad at all."
                        print str(random.randint(1, self.population)) + " people would dearly like to see you assassinated,"
                        print "but we all have our trivial problems.\n"

                print "<<-----------<END>----------->>"

def main():
        os.system('clear')      #clear screen

        get_input = test_get_input

        gameturns = get_input('How many years would you like to play? => ')
        if (gameturns == '') or (int(gameturns) <= 0):
                gameturns = 10
        sumer = CityState(int(gameturns))

        while int(gameturns) > 0:

                tmp = False
                while tmp == False:
                        tmp = sumer.get_acres()

                tmp = False
                while tmp == False:
                        tmp = sumer.feed_people()

                tmp = False
                while tmp == False:
                        tmp = sumer.plant_fields()

                gameturns = int(gameturns) - 1
                sumer.print_year_end()

        sumer.print_end_reign()

if __name__ == "__main__":
        main()
<|endoftext|>


<commit_before>
# !/usr/bin/env python
"""
insta_raider.py

usage: insta_raider.py [-h] -u USERNAME

@amirkurtovic

"""
import argparse
import logging
import os
import os.path as op
import re
import email.utils as eut
import requests
import calendar
try:
    from requests.packages.urllib3.exceptions import InsecurePlatformWarning
except ImportError:
    from urllib3.exceptions import InsecurePlatformWarning
import time
import warnings
import selenium.webdriver as webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.common.exceptions import NoSuchElementException
warnings.filterwarnings("ignore", category=InsecurePlatformWarning)

class PrivateUserError(Exception):
    """Raised if the profile is found to be private"""

try:
    import urlparse
except ImportError:
    import urllib.parse as urlparse

class InstaRaider(object):

    def __init__(self, username, directory, num_to_download=None,
                 log_level='info'):
        self.username = username
        self.profile_url = self.get_url(username)
        self.directory = directory
        self.PAUSE = 1
        self.user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
        self.headers = {'User-Agent': self.user_agent}
        self.html_source = None
        self.log_level = getattr(logging, log_level.upper())
        self.setup_logging(self.log_level)
        self.set_num_posts(num_to_download)
        self.setup_webdriver()

    def get_url(self, path):
        return urlparse.urljoin('https://instagram.com', path)

    def set_num_posts(self, num_to_download=None):
        self.num_posts = int(self.get_posts_count(self.profile_url) or 0)
        self.num_to_download = num_to_download

    def setup_logging(self, level=logging.INFO):
        self.logger = logging.getLogger('instaraider')
        self.logger.addHandler(logging.StreamHandler())
        self.logger.setLevel(logging.INFO)

    def log(self, *strings, **kwargs):
        level = kwargs.pop('level', logging.INFO)
        self.logger.log(level, u' '.join(str(s) for s in strings))

    def setup_webdriver(self):
        self.profile = webdriver.FirefoxProfile()
        self.profile.set_preference("general.useragent.override", self.user_agent)
        self.webdriver = webdriver.Firefox(self.profile)
        self.webdriver.set_window_size(480, 320)
        self.webdriver.set_window_position(800, 0)

    def get_posts_count(self, url):
        """
        Given a url to Instagram profile, return number of photos posted
        """
        response = requests.get(url)
        counts_code = re.search(r'\"media":{"count":\d+', response.text)
        if not counts_code:
            return None
        return re.findall(r'\d+', counts_code.group())[0]

    def log_in_user(self):
        driver = self.webdriver
        self.log('You need to login to access this profile.',
                 'Redirecting you to the login page in the browser.',
                 level=logging.WARN)
        driver.get(self.get_url('accounts/login/'))

        # Wait until user has been successfully logged in and redirceted
        # to his/her feed.
        WebDriverWait(driver, 60).until(
            expected_conditions.presence_of_element_located(
                (By.CSS_SELECTOR, '.-cx-PRIVATE-FeedPage__feed'),
            )
        )

        self.log('User successfully logged in.', level=logging.INFO)
        self.set_num_posts()  # Have to set this again
        driver.get(self.profile_url)

    def load_instagram(self):
        """
        Using Selenium WebDriver, load Instagram page to get page source

        """
        self.log(self.username, 'has', self.num_posts, 'posts on Instagram.')
        if self.num_to_download is not None:
            self.log("The first", self.num_to_download, "of them will be downloaded.")

        num_to_download = self.num_to_download or self.num_posts
        driver = self.webdriver
        # load Instagram profile and wait for PAUSE
        self.log("Loading Instagram profile...")
        driver.get(self.profile_url)
        driver.implicitly_wait(self.PAUSE)

        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        try:
            el = driver.find_element_by_css_selector(
                '.-cx-PRIVATE-ProfilePage__advisoryMessageHeader'
            )
        except NoSuchElementException:
            pass
        else:
            self.log_in_user()

        if (num_to_download > 24):
            scroll_to_bottom = self.get_scroll_count(num_to_download)
            element = driver.find_element_by_css_selector('div.-cx-PRIVATE-AutoloadingPostsGrid__moreLoadingIndicator a')
            driver.implicitly_wait(self.PAUSE)
            element.click()

            for y in range(int(scroll_to_bottom)):
                self.scroll_page(driver)

        # After load all profile photos, retur, source to download_photos()
        time.sleep(1)
        source = driver.page_source

        return source

    def scroll_page(self, driver):
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        time.sleep(0.2)
        driver.execute_script("window.scrollTo(0, 0);")

    def get_scroll_count(self, count):
        return (int(count) - 24) / 12 + 1

    def validate(self):
        """
        returns True if Instagram username is valid
        """
        req = requests.get(self.profile_url)

        try:
            req.raise_for_status()
        except:
            self.log('User', self.username, 'is not valid.',
                     level=logging.ERROR)
            return False

        if not self.num_posts:
            self.log('User', self.username, 'has no photos to download.',
                     level=logging.ERROR)
            return False
        return True

    def save_photo(self, photo_url, photo_name):
        image_request = requests.get(photo_url, headers=self.headers)
        image_data = image_request.content
        with open(photo_name, 'wb') as fp:
            fp.write(image_data)

        if "last-modified" in image_request.headers:
            modtime = calendar.timegm(eut.parsedate(image_request.headers["last-modified"]))
            os.utime(photo_name, (modtime, modtime))

    def download_photos(self):
        """
        Given source code for loaded Instagram page,
        extract all hrefs and download full-resolution photos

        source: HTML source code of Instagram profile papge
        """
        num_to_download = self.num_to_download or self.num_posts
        if self.html_source is None:
            self.html_source = self.load_instagram()

        # check if directory exists, if not, make it
        if not op.exists(self.directory):
            os.makedirs(self.directory)

        # index for progress bar
        photos_saved = 0
        self.log("Saving photos to", self.directory)

        links = re.findall(r'src="[https]+:...[\/\w \.-]*..[\/\w \.-]*..[\/\w \.-]*..[\/\w \.-].jpg', self.html_source)

        for link in links:
            photo_url = link[5:]
            photo_url = photo_url.replace('\\', '')
            photo_url = re.sub(r'/s\d+x\d+/', '/', photo_url)

            split = urlparse.urlsplit(photo_url)
            photo_name = op.join(self.directory, split.path.split("/")[-1])

            # save full-resolution photo if its new
            if not op.isfile(photo_name):
                self.save_photo(photo_url, photo_name)
                photos_saved += 1
                self.log('Downloaded file {}/{} ({}).'.format(
                    photos_saved, num_to_download, op.basename(photo_name)))
            else:
                self.log('Skipping file', photo_name, 'as it already exists.')

            if photos_saved >= num_to_download:
                break

        self.log('Saved', photos_saved, 'files to', self.directory)

    def download_videos(self):
        num_to_download = self.num_to_download or self.num_posts
        if self.html_source is None:
            self.html_source = self.load_instagram()
        if not op.exists(self.directory):
            os.makedirs(self.directory)
        driver = self.webdriver
        videos = driver.find_elements_by_xpath('.//*[@id="react-root"]/section/main/article/div/div[1]/div/a[.//*[@Class="-cx-PRIVATE-PostsGridItem__videoIndicatorWrapper"]]')
        driver.implicitly_wait(2)
        videos_links = [link.get_attribute("href") for link in videos]
        for video in videos_links:
            driver.get(video)
            video_url = re.search(r'src="([https]+:...[\/\w \.-]*..[\/\w \.-]*..[\/\w \.-]*..[\/\w \.-]\.mp4)', driver.page_source)
            video_url = video_url.group(1)
            video_url = video_url.replace("\\", "")
            split = urlparse.urlsplit(video_url)
            video_name = op.join(self.directory, split.path.split("/")[-1])

            self.save_photo(video_url, video_name)

    def download_media(self):
        self.download_photos()
        self.download_videos()
        self.webdriver.close()

def main():
    # parse arguments
    parser = argparse.ArgumentParser(description='InstaRaider')
    parser.add_argument('username', help='Instagram username')
    parser.add_argument('directory', help='Where to save the images')
    parser.add_argument('-n', '--num-to-download',
                        help='Number of posts to download', type=int)
    parser.add_argument('-l', '--log-level', help="Log level", default='info')
    args = parser.parse_args()
    username = args.username
    directory = op.expanduser(args.directory)

    raider = InstaRaider(username, directory,
                         num_to_download=args.num_to_download,
                         log_level=args.log_level)

    if not raider.validate():
        return

    raider.download_media()

if __name__ == '__main__':
    main()
<commit_msg>Revert "Insta raider video downloader"<commit_after>#!/usr/bin/env python
"""
insta_raider.py

usage: insta_raider.py [-h] -u USERNAME

@amirkurtovic

"""
import argparse
import logging
import os
import os.path as op
import re
import email.utils as eut
import requests
import calendar
try:
    from requests.packages.urllib3.exceptions import InsecurePlatformWarning
except ImportError:
    from urllib3.exceptions import InsecurePlatformWarning
import time
import warnings
import selenium.webdriver as webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.common.exceptions import NoSuchElementException

warnings.filterwarnings("ignore", category=InsecurePlatformWarning)

class PrivateUserError(Exception):
    """Raised if the profile is found to be private"""

try:
    import urlparse
except ImportError:
    import urllib.parse as urlparse

class InstaRaider(object):

    def __init__(self, username, directory, num_to_download=None,
                 log_level='info'):
        self.username = username
        self.profile_url = self.get_url(username)
        self.directory = directory
        self.PAUSE = 1
        self.user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
        self.headers = {'User-Agent': self.user_agent}
        self.html_source = None
        self.log_level = getattr(logging, log_level.upper())
        self.setup_logging(self.log_level)
        self.set_num_posts(num_to_download)
        self.setup_webdriver()

    def get_url(self, path):
        return urlparse.urljoin('https://instagram.com', path)

    def set_num_posts(self, num_to_download=None):
        self.num_posts = int(self.get_posts_count(self.profile_url) or 0)
        self.num_to_download = num_to_download

    def setup_logging(self, level=logging.INFO):
        self.logger = logging.getLogger('instaraider')
        self.logger.addHandler(logging.StreamHandler())
        self.logger.setLevel(logging.INFO)

    def log(self, *strings, **kwargs):
        level = kwargs.pop('level', logging.INFO)
        self.logger.log(level, u' '.join(str(s) for s in strings))

    def setup_webdriver(self):
        self.profile = webdriver.FirefoxProfile()
        self.profile.set_preference("general.useragent.override", self.user_agent)
        self.webdriver = webdriver.Firefox(self.profile)
        self.webdriver.set_window_size(480, 320)
        self.webdriver.set_window_position(800, 0)

    def get_posts_count(self, url):
        """
        Given a url to Instagram profile, return number of photos posted
        """
        response = requests.get(url)
        counts_code = re.search(r'\"media":{"count":\d+', response.text)
        if not counts_code:
            return None
        return re.findall(r'\d+', counts_code.group())[0]

    def log_in_user(self):
        driver = self.webdriver
        self.log('You need to login to access this profile.',
                 'Redirecting you to the login page in the browser.',
                 level=logging.WARN)
        driver.get(self.get_url('accounts/login/'))

        # Wait until user has been successfully logged in and redirceted
        # to his/her feed.
        WebDriverWait(driver, 60).until(
            expected_conditions.presence_of_element_located(
                (By.CSS_SELECTOR, '.-cx-PRIVATE-FeedPage__feed'),
            )
        )

        self.log('User successfully logged in.', level=logging.INFO)
        self.set_num_posts()  # Have to set this again
        driver.get(self.profile_url)

    def load_instagram(self):
        """
        Using Selenium WebDriver, load Instagram page to get page source

        """
        self.log(self.username, 'has', self.num_posts, 'posts on Instagram.')
        if self.num_to_download is not None:
            self.log("The first", self.num_to_download, "of them will be downloaded.")

        num_to_download = self.num_to_download or self.num_posts
        driver = self.webdriver
        # load Instagram profile and wait for PAUSE
        self.log("Loading Instagram profile...")
        driver.get(self.profile_url)
        driver.implicitly_wait(self.PAUSE)

        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        try:
            el = driver.find_element_by_css_selector(
                '.-cx-PRIVATE-ProfilePage__advisoryMessageHeader'
            )
        except NoSuchElementException:
            pass
        else:
            self.log_in_user()

        if (num_to_download > 24):
            scroll_to_bottom = self.get_scroll_count(num_to_download)
            element = driver.find_element_by_css_selector('div.-cx-PRIVATE-AutoloadingPostsGrid__moreLoadingIndicator a')
            driver.implicitly_wait(self.PAUSE)
            element.click()

            for y in range(int(scroll_to_bottom)):
                self.scroll_page(driver)

        # After load all profile photos, retur, source to download_photos()
        time.sleep(1)
        source = driver.page_source

        # close Firefox window
        driver.close()

        return source

    def scroll_page(self, driver):
        driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
        time.sleep(0.2)
        driver.execute_script("window.scrollTo(0, 0);")

    def get_scroll_count(self, count):
        return (int(count) - 24) / 12 + 1

    def validate(self):
        """
        returns True if Instagram username is valid
        """
        req = requests.get(self.profile_url)

        try:
            req.raise_for_status()
        except:
            self.log('User', self.username, 'is not valid.',
                     level=logging.ERROR)
            return False

        if not self.num_posts:
            self.log('User', self.username, 'has no photos to download.',
                     level=logging.ERROR)
            return False
        return True

    def save_photo(self, photo_url, photo_name):
        image_request = requests.get(photo_url, headers=self.headers)
        image_data = image_request.content
        with open(photo_name, 'wb') as fp:
            fp.write(image_data)

        if "last-modified" in image_request.headers:
            modtime = calendar.timegm(eut.parsedate(image_request.headers["last-modified"]))
            os.utime(photo_name, (modtime, modtime))

    def download_photos(self):
        """
        Given source code for loaded Instagram page,
        extract all hrefs and download full-resolution photos

        source: HTML source code of Instagram profile papge
        """
        num_to_download = self.num_to_download or self.num_posts
        if self.html_source is None:
            self.html_source = self.load_instagram()

        # check if directory exists, if not, make it
        if not op.exists(self.directory):
            os.makedirs(self.directory)

        # index for progress bar
        photos_saved = 0
        self.log("Saving photos to", self.directory)

        links = re.findall(r'src="[https]+:...[\/\w \.-]*..[\/\w \.-]*..[\/\w \.-]*..[\/\w \.-].jpg', self.html_source)

        for link in links:
            photo_url = link[5:]
            photo_url = photo_url.replace('\\', '')
            photo_url = re.sub(r'/s\d+x\d+/', '/', photo_url)

            split = urlparse.urlsplit(photo_url)
            photo_name = op.join(self.directory, split.path.split("/")[-1])

            # save full-resolution photo if its new
            if not op.isfile(photo_name):
                self.save_photo(photo_url, photo_name)
                photos_saved += 1
                self.log('Downloaded file {}/{} ({}).'.format(
                    photos_saved, num_to_download, op.basename(photo_name)))
            else:
                self.log('Skipping file', photo_name, 'as it already exists.')

            if photos_saved >= num_to_download:
                break

        self.log('Saved', photos_saved, 'files to', self.directory)

def main():
    # parse arguments
    parser = argparse.ArgumentParser(description='InstaRaider')
    parser.add_argument('username', help='Instagram username')
    parser.add_argument('directory', help='Where to save the images')
    parser.add_argument('-n', '--num-to-download',
                        help='Number of posts to download', type=int)
    parser.add_argument('-l', '--log-level', help="Log level", default='info')
    args = parser.parse_args()
    username = args.username
    directory = op.expanduser(args.directory)

    raider = InstaRaider(username, directory,
                         num_to_download=args.num_to_download,
                         log_level=args.log_level)

    if not raider.validate():
        return

    raider.download_photos()

if __name__ == '__main__':
    main()
<|endoftext|>