<commit_before># -*- coding: utf-8 -*-
# Copyright (C) 2004-2008 Tristan Seligmann and Jonathan Jacobs
# Copyright (C) 2012-2014 Bastian Kleineidam
# Copyright (C) 2015-2019 Tobias Gruetzmacher

from __future__ import absolute_import, division, print_function

from re import compile, escape

from ..scraper import _BasicScraper
from ..util import tagre
from ..helpers import bounceStarter, indirectStarter
from .common import _ComicControlScraper, _WordPressScraper, _WPNaviIn

class HagarTheHorrible(_BasicScraper):
    url = 'http://www.hagarthehorrible.net/'
    stripUrl = 'http://www.hagardunor.net/comicstrips_us.php?serietype=9&colortype=1&serieno=%s'
    firstStripUrl = stripUrl % '1'
    multipleImagesPerStrip = True
    imageSearch = compile(tagre("img", "src", r'(stripus\d+/(?:Hagar_The_Horrible_?|h)\d+[^ >]+)', quote=""))
    prevUrl = r'(comicstrips_us\.php\?serietype\=9\&colortype\=1\&serieno\=\d+)'
    prevSearch = compile(tagre("a", "href", prevUrl, after="Previous"))
    help = 'Index format: number'

    def starter(self):
        """Return last gallery link."""
        url = 'http://www.hagardunor.net/comics.php'
        data = self.getPage(url)
        pattern = compile(tagre("a", "href", self.prevUrl))
        for starturl in self.fetchUrls(url, data, pattern):
            pass
        return starturl

# "Hiatus", navigation missing
class _HappyJar(_WordPressScraper):
    url = 'http://www.happyjar.com/'

class HarkAVagrant(_BasicScraper):
    url = 'http://www.harkavagrant.com/'
    rurl = escape(url)
    starter = bounceStarter
    stripUrl = url + 'index.php?id=%s'
    firstStripUrl = stripUrl % '1'
    imageSearch = compile(tagre("img", "src", r'(%s[^"]+)' % rurl,
                                after='BORDER'))
    prevSearch = compile(tagre("a", "href", r'(%sindex\.php\?id=\d+)' % rurl) +
                         tagre("img", "src", "buttonprevious.png"))
    nextSearch = compile(tagre("a", "href", r'(%sindex\.php\?id=\d+)' % rurl) +
                         tagre("img", "src", "buttonnext.png"))
    help = 'Index format: number'

    def namer(self, image_url, page_url):
        filename = image_url.rsplit('/', 1)[1]
        num = page_url.rsplit('=', 1)[1]
        return '%s-%s' % (num, filename)

class HavocInc(_WordPressScraper):
    url = 'http://www.radiocomix.com/havoc-inc/'
    stripUrl = url + 'comic/%s/'
    firstStripUrl = stripUrl % 'havoc-cover'

class HeyFox(_WordPressScraper):
    url = 'http://www.steamclaw.com/heyfox/'
    stripUrl = url + 'archives/comic/%s'
    firstStripUrl = stripUrl % '11092004'
    adult = True

class Hipsters(_WordPressScraper):
    url = 'http://www.hipsters-comic.com/'
    firstStripUrl = 'http://www.hipsters-comic.com/comic/hip01/'

class HijinksEnsue(_WPNaviIn):
    url = 'http://hijinksensue.com/'
    latestSearch = '//a[text()="Latest HijiNKS ENSUE"]'
    firstStripUrl = 'http://hijinksensue.com/comic/who-is-your-daddy-and-what-does-he-do/'
    starter = indirectStarter

class HijinksEnsueClassic(_WPNaviIn):
    url = 'http://hijinksensue.com/comic/open-your-eyes/'
    firstStripUrl = 'http://hijinksensue.com/comic/a-soul-as-black-as-eyeliner/'
    endOfLife = True

class HijinksEnsueConvention(_WPNaviIn):
    url = 'http://hijinksensue.com/comic/emerald-city-comicon-2015-fancy-sketches-part-4/'
    firstStripUrl = 'http://hijinksensue.com/comic/whatever-dad-im-outta-here/'
    endOfLife = True

class HijinksEnsuePhoto(_WPNaviIn):
    url = 'http://hijinksensue.com/comic/emerald-city-comicon-2015-fancy-photo-comic-part-2/'
    firstStripUrl = 'http://hijinksensue.com/comic/san-diego-comic-con-fancy-picto-comic-pt-1/'
    endOfLife = True

class Housepets(_WordPressScraper):
    url = 'http://www.housepetscomic.com/'
    stripUrl = url + 'comic/%s/'
    firstStripUrl = '2008/06/02/when-boredom-strikes'

class HowToBeAWerewolf(_ComicControlScraper):
    url = 'http://howtobeawerewolf.com/'
    stripUrl = url + 'comic/%s'
    firstStripUrl = stripUrl % 'coming-february-3rd'

    def namer(self, imageUrl, pageUrl):
        filename = imageUrl.rsplit('/', 1)[-1]
        if filename[0].isdigit():
            filename = filename.split('-', 1)[1]
        return filename
<commit_msg>Add HeyKitty<commit_after># -*- coding: utf-8 -*-
# Copyright (C) 2004-2008 Tristan Seligmann and Jonathan Jacobs
# Copyright (C) 2012-2014 Bastian Kleineidam
# Copyright (C) 2015-2019 Tobias Gruetzmacher

from __future__ import absolute_import, division, print_function

from re import compile, escape

from ..scraper import _BasicScraper
from ..util import tagre
from ..helpers import bounceStarter, indirectStarter
from .common import _ComicControlScraper, _WordPressScraper, _WPNaviIn

class HagarTheHorrible(_BasicScraper):
    url = 'http://www.hagarthehorrible.net/'
    stripUrl = 'http://www.hagardunor.net/comicstrips_us.php?serietype=9&colortype=1&serieno=%s'
    firstStripUrl = stripUrl % '1'
    multipleImagesPerStrip = True
    imageSearch = compile(tagre("img", "src", r'(stripus\d+/(?:Hagar_The_Horrible_?|h)\d+[^ >]+)', quote=""))
    prevUrl = r'(comicstrips_us\.php\?serietype\=9\&colortype\=1\&serieno\=\d+)'
    prevSearch = compile(tagre("a", "href", prevUrl, after="Previous"))
    help = 'Index format: number'

    def starter(self):
        """Return last gallery link."""
        url = 'http://www.hagardunor.net/comics.php'
        data = self.getPage(url)
        pattern = compile(tagre("a", "href", self.prevUrl))
        for starturl in self.fetchUrls(url, data, pattern):
            pass
        return starturl

# "Hiatus", navigation missing
class _HappyJar(_WordPressScraper):
    url = 'http://www.happyjar.com/'

class HarkAVagrant(_BasicScraper):
    url = 'http://www.harkavagrant.com/'
    rurl = escape(url)
    starter = bounceStarter
    stripUrl = url + 'index.php?id=%s'
    firstStripUrl = stripUrl % '1'
    imageSearch = compile(tagre("img", "src", r'(%s[^"]+)' % rurl,
                                after='BORDER'))
    prevSearch = compile(tagre("a", "href", r'(%sindex\.php\?id=\d+)' % rurl) +
                         tagre("img", "src", "buttonprevious.png"))
    nextSearch = compile(tagre("a", "href", r'(%sindex\.php\?id=\d+)' % rurl) +
                         tagre("img", "src", "buttonnext.png"))
    help = 'Index format: number'

    def namer(self, image_url, page_url):
        filename = image_url.rsplit('/', 1)[1]
        num = page_url.rsplit('=', 1)[1]
        return '%s-%s' % (num, filename)

class HavocInc(_WordPressScraper):
    url = 'http://www.radiocomix.com/havoc-inc/'
    stripUrl = url + 'comic/%s/'
    firstStripUrl = stripUrl % 'havoc-cover'

class HeyFox(_WordPressScraper):
    url = 'http://www.steamclaw.com/heyfox/'
    stripUrl = url + 'archives/comic/%s'
    firstStripUrl = stripUrl % '11092004'
    adult = True

class HeyKitty(_WordPressScraper):
    url = 'http://heykittycomic.com/'
    stripUrl = url + '?comic=%s'
    firstStripUrl = stripUrl % 'it-begins'

class Hipsters(_WordPressScraper):
    url = 'http://www.hipsters-comic.com/'
    firstStripUrl = 'http://www.hipsters-comic.com/comic/hip01/'

class HijinksEnsue(_WPNaviIn):
    url = 'http://hijinksensue.com/'
    latestSearch = '//a[text()="Latest HijiNKS ENSUE"]'
    firstStripUrl = 'http://hijinksensue.com/comic/who-is-your-daddy-and-what-does-he-do/'
    starter = indirectStarter

class HijinksEnsueClassic(_WPNaviIn):
    url = 'http://hijinksensue.com/comic/open-your-eyes/'
    firstStripUrl = 'http://hijinksensue.com/comic/a-soul-as-black-as-eyeliner/'
    endOfLife = True

class HijinksEnsueConvention(_WPNaviIn):
    url = 'http://hijinksensue.com/comic/emerald-city-comicon-2015-fancy-sketches-part-4/'
    firstStripUrl = 'http://hijinksensue.com/comic/whatever-dad-im-outta-here/'
    endOfLife = True

class HijinksEnsuePhoto(_WPNaviIn):
    url = 'http://hijinksensue.com/comic/emerald-city-comicon-2015-fancy-photo-comic-part-2/'
    firstStripUrl = 'http://hijinksensue.com/comic/san-diego-comic-con-fancy-picto-comic-pt-1/'
    endOfLife = True

class Housepets(_WordPressScraper):
    url = 'http://www.housepetscomic.com/'
    stripUrl = url + 'comic/%s/'
    firstStripUrl = '2008/06/02/when-boredom-strikes'

class HowToBeAWerewolf(_ComicControlScraper):
    url = 'http://howtobeawerewolf.com/'
    stripUrl = url + 'comic/%s'
    firstStripUrl = stripUrl % 'coming-february-3rd'

    def namer(self, imageUrl, pageUrl):
        filename = imageUrl.rsplit('/', 1)[-1]
        if filename[0].isdigit():
            filename = filename.split('-', 1)[1]
        return filename
<|endoftext|>


<commit_before>import urllib.request
import subprocess

LIMIT = 10
PLAYER = 'vlc'
url = 'http://streams.twitch.tv/kraken/streams?limit='+str(LIMIT)+'&offset=0&game=Music&broadcaster_language=&on_site=1'

with urllib.request.urlopen(url) as response:
    html = response.read().decode('utf8')

i = 0
urls = []
for line in html.split(','):
    if 'display_name' in line:
        name = line.split('"')[-2]
        print(str(i) + ') ' + name)
        i += 1
    if 'url' in line:
        url = line.split('"')[-2]
        urls.append(url)

choice = LIMIT
while (choice >= LIMIT):
    choice = int(input('Choose a stream\n'))

cmd = ['livestreamer', urls[choice], 'audio']
if PLAYER != 'vlc':
    cmd.append('-p')
    cmd.append(PLAYER)

subprocess.Popen(cmd, shell=False)
<commit_msg>Display stream title<commit_after>import urllib.request
import subprocess

LIMIT = 10
PLAYER = 'vlc'
url = 'http://streams.twitch.tv/kraken/streams?limit='+str(LIMIT)+'&offset=0&game=Music&broadcaster_language=&on_site=1'

with urllib.request.urlopen(url) as response:
    html = response.read().decode('utf8')

i = 0
urls = []
for line in html.split(','):
    if 'status' in line:
        status = line.split('"')[-2]
        status = ''.join(i for i in status if ord(i)<128) #filter non ascii characters
    if 'display_name' in line:
        name = line.split('"')[-2]
        print(str(i) + ') ' + name + ' : ' + status)
        i += 1
    if 'url' in line:
        url = line.split('"')[-2]
        urls.append(url)

choice = LIMIT
while (choice >= LIMIT):
    choice = int(input('Choose a stream\n'))

cmd = ['livestreamer', urls[choice], 'audio']
if PLAYER != 'vlc':
    cmd.append('-p')
    cmd.append(PLAYER)

subprocess.Popen(cmd, shell=False)
<|endoftext|>


<commit_before>import datetime, decimal
from functools import wraps
from uuid import uuid1
from jsonrpc._json import loads, dumps
from jsonrpc.exceptions import *
from jsonrpc.types import *
from django.core import signals
empty_dec = lambda f: f
try:
  from django.views.decorators.csrf import csrf_exempt
except (NameError, ImportError):
  csrf_exempt = empty_dec

from django.core.serializers.json import DjangoJSONEncoder

NoneType = type(None)
encode_kw = lambda p: dict([(str(k), v) for k, v in p.iteritems()])

def encode_kw11(p):
  if not type(p) is dict:
    return {}
  ret = p.copy()
  removes = []
  for k, v in ret.iteritems():
    try:
      int(k)
    except ValueError:
      pass
    else:
      removes.append(k)
  for k in removes:
    ret.pop(k)
  return ret

def encode_arg11(p):
  if type(p) is list:
    return p
  elif not type(p) is dict:
    return []
  else:
    pos = []
    d = encode_kw(p)
    for k, v in d.iteritems():
      try:
        pos.append(int(k))
      except ValueError:
        pass
    pos = list(set(pos))
    pos.sort()
    return [d[str(i)] for i in pos]

def validate_params(method, D):
  if type(D['params']) == Object:
    keys = method.json_arg_types.keys()
    if len(keys) != len(D['params']):
      raise InvalidParamsError('Not enough params provided for %s' % method.json_sig)
    for k in keys:
      if not k in D['params']:
        raise InvalidParamsError('%s is not a valid parameter for %s'
                                 % (k, method.json_sig))
      if not Any.kind(D['params'][k]) == method.json_arg_types[k]:
        raise InvalidParamsError('%s is not the correct type %s for %s'
          % (type(D['params'][k]), method.json_arg_types[k], method.json_sig))
  elif type(D['params']) == Array:
    arg_types = method.json_arg_types.values()
    try:
      for i, arg in enumerate(D['params']):
        if not Any.kind(arg) == arg_types[i]:
          raise InvalidParamsError('%s is not the correct type %s for %s'
            % (type(arg), arg_types[i], method.json_sig))
    except IndexError:
      raise InvalidParamsError('Too many params provided for %s' % method.json_sig)
    else:
      if len(D['params']) != len(arg_types):
        raise InvalidParamsError('Not enough params provided for %s' % method.json_sig)

class JSONRPCSite(object):
  "A JSON-RPC Site"
  def __init__(self, json_encoder=DjangoJSONEncoder):
    self.urls = {}
    self.uuid = str(uuid1())
    self.version = '1.0'
    self.name = 'django-json-rpc'
    self.register('system.describe', self.describe)
    self.set_json_encoder(json_encoder)

  def set_json_encoder(self, json_encoder=DjangoJSONEncoder):
    self.json_encoder = json_encoder

  def register(self, name, method):
    self.urls[unicode(name)] = method

  def empty_response(self, version='1.0'):
    resp = {'id': None}
    if version == '1.1':
      resp['version'] = version
      return resp
    if version == '2.0':
      resp['jsonrpc'] = version
    resp.update({'error': None, 'result': None})
    return resp

  def validate_get(self, request, method):
    encode_get_params = lambda r: dict([(k, v[0] if len(v) == 1 else v)
                                         for k, v in r])
    if request.method == 'GET':
      method = unicode(method)
      if method in self.urls and getattr(self.urls[method], 'json_safe', False):
        D = {
          'params': encode_get_params(request.GET.lists()),
          'method': method,
          'id': 'jsonrpc',
          'version': '1.1'
        }
        return True, D
    return False, {}

  def response_dict(self, request, D, is_batch=False, version_hint='1.0', json_encoder=None):
    json_encoder = json_encoder or self.json_encoder
    version = version_hint
    response = self.empty_response(version=version)
    apply_version = {'2.0': lambda f, r, p: f(r, **encode_kw(p)) if type(p) is dict else f(r, *p),
                     '1.1': lambda f, r, p: f(r, *encode_arg11(p), **encode_kw(encode_kw11(p))),
                     '1.0': lambda f, r, p: f(r, *p)}

    try:
      # params: An Array or Object, that holds the actual parameter values
      # for the invocation of the procedure. Can be omitted if empty.
      if 'params' not in D:
         D['params'] = []
      if 'method' not in D or 'params' not in D:
        raise InvalidParamsError('Request requires str:"method" and list:"params"')
      if D['method'] not in self.urls:
        raise MethodNotFoundError('Method not found. Available methods: %s' % (
                        '\n'.join(self.urls.keys())))

      if 'jsonrpc' in D:
        if str(D['jsonrpc']) not in apply_version:
          raise InvalidRequestError('JSON-RPC version %s not supported.' % D['jsonrpc'])
        version = request.jsonrpc_version = response['jsonrpc'] = str(D['jsonrpc'])
      elif 'version' in D:
        if str(D['version']) not in apply_version:
          raise InvalidRequestError('JSON-RPC version %s not supported.' % D['version'])
        version = request.jsonrpc_version = response['version'] = str(D['version'])
      else:
        request.jsonrpc_version = '1.0'

      method = self.urls[str(D['method'])]
      if getattr(method, 'json_validate', False):
        validate_params(method, D)

      if 'id' in D and D['id'] is not None: # regular request
        response['id'] = D['id']
        if version in ('1.1', '2.0') and 'error' in response:
          response.pop('error')
      elif is_batch: # notification, not ok in a batch format, but happened anyway
        raise InvalidRequestError

      R = apply_version[version](method, request, D['params'])

      if 'id' not in D or ('id' in D and D['id'] is None): # notification
        return None, 204

      encoder = json_encoder()
      if not sum(map(lambda e: isinstance(R, e), # type of `R` should be one of these or...
         (dict, str, unicode, int, long, list, set, NoneType, bool))):
        try:
          rs = encoder.default(R) # ...or something this thing supports
        except TypeError, exc:
          raise TypeError("Return type not supported, for %r" % R)

      response['result'] = R

      status = 200

    except Error, e:
      signals.got_request_exception.send(sender=self.__class__, request=request)
      response['error'] = e.json_rpc_format
      if version in ('1.1', '2.0') and 'result' in response:
        response.pop('result')
      status = e.status
    except Exception, e:
      # exception missed by others
      signals.got_request_exception.send(sender=self.__class__, request=request)
      other_error = OtherError(e)
      response['error'] = other_error.json_rpc_format
      status = other_error.status
      if version in ('1.1', '2.0') and 'result' in response:
        response.pop('result')

    # Exactly one of result or error MUST be specified. It's not
    # allowed to specify both or none.
    if version in ('1.1', '2.0') and 'error' in response and not response['error']:
      response.pop('error')

    return response, status

  @csrf_exempt
  def dispatch(self, request, method='', json_encoder=None):
    from django.http import HttpResponse
    json_encoder = json_encoder or self.json_encoder

    try:
      # in case we do something json doesn't like, we always get back valid json-rpc response
      response = self.empty_response()
      if request.method.lower() == 'get':
        valid, D = self.validate_get(request, method)
        if not valid:
          raise InvalidRequestError('The method you are trying to access is '
                                    'not available by GET requests')
      elif not request.method.lower() == 'post':
        raise RequestPostError
      else:
        try:
          D = loads(request.raw_post_data)
        except:
          raise InvalidRequestError

      if type(D) is list:
        response = [self.response_dict(request, d, is_batch=True, json_encoder=json_encoder)[0] for d in D]
        status = 200
      else:
        response, status = self.response_dict(request, D, json_encoder=json_encoder)
        if response is None and (not u'id' in D or D[u'id'] is None): # a notification
          return HttpResponse('', status=status)

      json_rpc = dumps(response, cls=json_encoder)
    except Error, e:
      signals.got_request_exception.send(sender=self.__class__, request=request)
      response['error'] = e.json_rpc_format
      status = e.status
      json_rpc = dumps(response, cls=json_encoder)
    except Exception, e:
      # exception missed by others
      signals.got_request_exception.send(sender=self.__class__, request=request)
      other_error = OtherError(e)
      response['result'] = None
      response['error'] = other_error.json_rpc_format
      status = other_error.status

      json_rpc = dumps(response,cls=json_encoder)

    return HttpResponse(json_rpc, status=status, content_type='application/json-rpc')

  def procedure_desc(self, key):
    M = self.urls[key]
    return {
      'name': M.json_method,
      'summary': M.__doc__,
      'idempotent': M.json_safe,
      'params': [{'type': str(Any.kind(t)), 'name': k}
        for k, t in M.json_arg_types.iteritems()],
      'return': {'type': M.json_return_type}}

  def service_desc(self):
    return {
      'sdversion': '1.0',
      'name': self.name,
      'id': 'urn:uuid:%s' % str(self.uuid),
      'summary': self.__doc__,
      'version': self.version,
      'procs': [self.procedure_desc(k)
        for k in self.urls.iterkeys()
          if self.urls[k] != self.describe]}

  def describe(self, request):
    return self.service_desc()

jsonrpc_site = JSONRPCSite()
<commit_msg>Stop triggering Django 1.5 DeprecationWarning<commit_after>import datetime, decimal
from functools import wraps
from uuid import uuid1
from jsonrpc._json import loads, dumps
from jsonrpc.exceptions import *
from jsonrpc.types import *
from django.core import signals
empty_dec = lambda f: f
try:
  from django.views.decorators.csrf import csrf_exempt
except (NameError, ImportError):
  csrf_exempt = empty_dec

from django.core.serializers.json import DjangoJSONEncoder

NoneType = type(None)
encode_kw = lambda p: dict([(str(k), v) for k, v in p.iteritems()])

def encode_kw11(p):
  if not type(p) is dict:
    return {}
  ret = p.copy()
  removes = []
  for k, v in ret.iteritems():
    try:
      int(k)
    except ValueError:
      pass
    else:
      removes.append(k)
  for k in removes:
    ret.pop(k)
  return ret

def encode_arg11(p):
  if type(p) is list:
    return p
  elif not type(p) is dict:
    return []
  else:
    pos = []
    d = encode_kw(p)
    for k, v in d.iteritems():
      try:
        pos.append(int(k))
      except ValueError:
        pass
    pos = list(set(pos))
    pos.sort()
    return [d[str(i)] for i in pos]

def validate_params(method, D):
  if type(D['params']) == Object:
    keys = method.json_arg_types.keys()
    if len(keys) != len(D['params']):
      raise InvalidParamsError('Not enough params provided for %s' % method.json_sig)
    for k in keys:
      if not k in D['params']:
        raise InvalidParamsError('%s is not a valid parameter for %s'
                                 % (k, method.json_sig))
      if not Any.kind(D['params'][k]) == method.json_arg_types[k]:
        raise InvalidParamsError('%s is not the correct type %s for %s'
          % (type(D['params'][k]), method.json_arg_types[k], method.json_sig))
  elif type(D['params']) == Array:
    arg_types = method.json_arg_types.values()
    try:
      for i, arg in enumerate(D['params']):
        if not Any.kind(arg) == arg_types[i]:
          raise InvalidParamsError('%s is not the correct type %s for %s'
            % (type(arg), arg_types[i], method.json_sig))
    except IndexError:
      raise InvalidParamsError('Too many params provided for %s' % method.json_sig)
    else:
      if len(D['params']) != len(arg_types):
        raise InvalidParamsError('Not enough params provided for %s' % method.json_sig)

class JSONRPCSite(object):
  "A JSON-RPC Site"
  def __init__(self, json_encoder=DjangoJSONEncoder):
    self.urls = {}
    self.uuid = str(uuid1())
    self.version = '1.0'
    self.name = 'django-json-rpc'
    self.register('system.describe', self.describe)
    self.set_json_encoder(json_encoder)

  def set_json_encoder(self, json_encoder=DjangoJSONEncoder):
    self.json_encoder = json_encoder

  def register(self, name, method):
    self.urls[unicode(name)] = method

  def empty_response(self, version='1.0'):
    resp = {'id': None}
    if version == '1.1':
      resp['version'] = version
      return resp
    if version == '2.0':
      resp['jsonrpc'] = version
    resp.update({'error': None, 'result': None})
    return resp

  def validate_get(self, request, method):
    encode_get_params = lambda r: dict([(k, v[0] if len(v) == 1 else v)
                                         for k, v in r])
    if request.method == 'GET':
      method = unicode(method)
      if method in self.urls and getattr(self.urls[method], 'json_safe', False):
        D = {
          'params': encode_get_params(request.GET.lists()),
          'method': method,
          'id': 'jsonrpc',
          'version': '1.1'
        }
        return True, D
    return False, {}

  def response_dict(self, request, D, is_batch=False, version_hint='1.0', json_encoder=None):
    json_encoder = json_encoder or self.json_encoder
    version = version_hint
    response = self.empty_response(version=version)
    apply_version = {'2.0': lambda f, r, p: f(r, **encode_kw(p)) if type(p) is dict else f(r, *p),
                     '1.1': lambda f, r, p: f(r, *encode_arg11(p), **encode_kw(encode_kw11(p))),
                     '1.0': lambda f, r, p: f(r, *p)}

    try:
      # params: An Array or Object, that holds the actual parameter values
      # for the invocation of the procedure. Can be omitted if empty.
      if 'params' not in D:
         D['params'] = []
      if 'method' not in D or 'params' not in D:
        raise InvalidParamsError('Request requires str:"method" and list:"params"')
      if D['method'] not in self.urls:
        raise MethodNotFoundError('Method not found. Available methods: %s' % (
                        '\n'.join(self.urls.keys())))

      if 'jsonrpc' in D:
        if str(D['jsonrpc']) not in apply_version:
          raise InvalidRequestError('JSON-RPC version %s not supported.' % D['jsonrpc'])
        version = request.jsonrpc_version = response['jsonrpc'] = str(D['jsonrpc'])
      elif 'version' in D:
        if str(D['version']) not in apply_version:
          raise InvalidRequestError('JSON-RPC version %s not supported.' % D['version'])
        version = request.jsonrpc_version = response['version'] = str(D['version'])
      else:
        request.jsonrpc_version = '1.0'

      method = self.urls[str(D['method'])]
      if getattr(method, 'json_validate', False):
        validate_params(method, D)

      if 'id' in D and D['id'] is not None: # regular request
        response['id'] = D['id']
        if version in ('1.1', '2.0') and 'error' in response:
          response.pop('error')
      elif is_batch: # notification, not ok in a batch format, but happened anyway
        raise InvalidRequestError

      R = apply_version[version](method, request, D['params'])

      if 'id' not in D or ('id' in D and D['id'] is None): # notification
        return None, 204

      encoder = json_encoder()
      if not sum(map(lambda e: isinstance(R, e), # type of `R` should be one of these or...
         (dict, str, unicode, int, long, list, set, NoneType, bool))):
        try:
          rs = encoder.default(R) # ...or something this thing supports
        except TypeError, exc:
          raise TypeError("Return type not supported, for %r" % R)

      response['result'] = R

      status = 200

    except Error, e:
      signals.got_request_exception.send(sender=self.__class__, request=request)
      response['error'] = e.json_rpc_format
      if version in ('1.1', '2.0') and 'result' in response:
        response.pop('result')
      status = e.status
    except Exception, e:
      # exception missed by others
      signals.got_request_exception.send(sender=self.__class__, request=request)
      other_error = OtherError(e)
      response['error'] = other_error.json_rpc_format
      status = other_error.status
      if version in ('1.1', '2.0') and 'result' in response:
        response.pop('result')

    # Exactly one of result or error MUST be specified. It's not
    # allowed to specify both or none.
    if version in ('1.1', '2.0') and 'error' in response and not response['error']:
      response.pop('error')

    return response, status

  @csrf_exempt
  def dispatch(self, request, method='', json_encoder=None):
    from django.http import HttpResponse
    json_encoder = json_encoder or self.json_encoder

    try:
      # in case we do something json doesn't like, we always get back valid json-rpc response
      response = self.empty_response()
      if request.method.lower() == 'get':
        valid, D = self.validate_get(request, method)
        if not valid:
          raise InvalidRequestError('The method you are trying to access is '
                                    'not available by GET requests')
      elif not request.method.lower() == 'post':
        raise RequestPostError
      else:
        try:
          D = loads(request.body)
        except:
          raise InvalidRequestError

      if type(D) is list:
        response = [self.response_dict(request, d, is_batch=True, json_encoder=json_encoder)[0] for d in D]
        status = 200
      else:
        response, status = self.response_dict(request, D, json_encoder=json_encoder)
        if response is None and (not u'id' in D or D[u'id'] is None): # a notification
          return HttpResponse('', status=status)

      json_rpc = dumps(response, cls=json_encoder)
    except Error, e:
      signals.got_request_exception.send(sender=self.__class__, request=request)
      response['error'] = e.json_rpc_format
      status = e.status
      json_rpc = dumps(response, cls=json_encoder)
    except Exception, e:
      # exception missed by others
      signals.got_request_exception.send(sender=self.__class__, request=request)
      other_error = OtherError(e)
      response['result'] = None
      response['error'] = other_error.json_rpc_format
      status = other_error.status

      json_rpc = dumps(response,cls=json_encoder)

    return HttpResponse(json_rpc, status=status, content_type='application/json-rpc')

  def procedure_desc(self, key):
    M = self.urls[key]
    return {
      'name': M.json_method,
      'summary': M.__doc__,
      'idempotent': M.json_safe,
      'params': [{'type': str(Any.kind(t)), 'name': k}
        for k, t in M.json_arg_types.iteritems()],
      'return': {'type': M.json_return_type}}

  def service_desc(self):
    return {
      'sdversion': '1.0',
      'name': self.name,
      'id': 'urn:uuid:%s' % str(self.uuid),
      'summary': self.__doc__,
      'version': self.version,
      'procs': [self.procedure_desc(k)
        for k in self.urls.iterkeys()
          if self.urls[k] != self.describe]}

  def describe(self, request):
    return self.service_desc()

jsonrpc_site = JSONRPCSite()
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# Copyright (C) 2004-2008 Tristan Seligmann and Jonathan Jacobs
# Copyright (C) 2012-2014 Bastian Kleineidam
# Copyright (C) 2015-2017 Tobias Gruetzmacher

from __future__ import absolute_import, division, print_function

from re import compile, escape, MULTILINE
try:
    from functools import cached_property
except ImportError:
    from cached_property import cached_property

from ..scraper import _BasicScraper, _ParserScraper
from ..helpers import indirectStarter, xpath_class
from ..util import tagre
from .common import _ComicControlScraper, _TumblrScraper, _WordPressScraper, _WPNavi, _WPNaviIn

class TalesOfTheQuestor(_ParserScraper):
    stripUrl = 'http://www.rhjunior.com/%s/'
    firstStripUrl = stripUrl % 'tales-of-the-questor-cover'
    url = stripUrl % 'comics/tales-of-the-questor'
    imageSearch = '//div[contains(@class, "entry-content")]//img'
    prevSearch = ('//a[@rel="prev"]', '//a[@title="Tales of the Questor"]')

class TheBrads(_ParserScraper):
    url = 'http://bradcolbow.com/archive/'
    imageSearch = '//div[%s]//img' % xpath_class('entry')
    prevSearch = '//a[%s]' % xpath_class('prev')
    multipleImagesPerStrip = True

class TheClassMenagerie(_ParserScraper):
    stripUrl = 'http://www.theclassm.com/d/%s.html'
    url = stripUrl % '20050717'
    firstStripUrl = stripUrl % '19990322'
    imageSearch = '//img[@class="ksc"]'
    prevSearch = '//a[@rel="prev"]'
    multipleImagesPerStrip = True
    endOfLife = True

class TheDevilsPanties(_WPNavi):
    url = 'http://thedevilspanties.com/'
    stripUrl = url + 'archives/%s'
    firstStripUrl = stripUrl % '300'
    help = 'Index format: number'

class TheDreamlandChronicles(_WordPressScraper):
    url = 'http://www.thedreamlandchronicles.com/'

class TheGamerCat(_ParserScraper):
    url = 'https://thegamercat.com/'
    stripUrl = url + 'comic/%s/'
    firstStripUrl = stripUrl % '06102011'
    imageSearch = '//div[@id="comic"]//img'
    prevSearch = '//a[contains(@class, "comic-nav-previous")]'
    help = 'Index format: stripname'

class TheGentlemansArmchair(_WordPressScraper):
    url = 'http://thegentlemansarmchair.com/'

class TheLandscaper(_BasicScraper):
    stripUrl = 'http://landscaper.visual-assault.net/comic/%s'
    url = stripUrl % 'latest'
    firstStripUrl = stripUrl % '1'
    imageSearch = compile(tagre("img", "src",
                                r'(/comics/comic/comic_page/[^"]+)'))
    prevSearch = compile(tagre("a", "href", r'(/comic/[^"]+)') +
                         '‹ Previous')
    help = 'Index format: name'

class TheMelvinChronicles(_WordPressScraper):
    url = 'http://melvin.jeaniebottle.com/'

class TheNoob(_WordPressScraper):
    url = 'http://thenoobcomic.com/'
    stripUrl = url + 'comic/%s/'
    firstStripUrl = stripUrl % '1'
    help = 'Index format: n (unpadded)'

class TheOrderOfTheStick(_BasicScraper):
    url = 'http://www.giantitp.com/'
    stripUrl = url + 'comics/oots%s.html'
    firstStripUrl = stripUrl % '0001'
    imageSearch = compile(r'')
    prevSearch = compile(r'<IMG src="/Images/redesign/ComicNav_Back.gif"')
    latestSearch = compile(r'<A href="(/comics/oots\d{4}\.html)"')
    help = 'Index format: n (unpadded)'
    starter = indirectStarter

    def namer(self, image_url, page_url):
        return page_url.rsplit('/', 1)[-1][:-5]

class TheRockCocks(_BasicScraper):
    url = 'http://rockcocks.slipshine.net/'
    rurl = escape(url)
    stripUrl = url + 'comics/%s'
    firstStripUrl = stripUrl % "page-1-nsfw-track-1-start"
    imageSearch = compile(tagre("img", "src", r'(%scomics/[^"]+)' % rurl, after='id="cc-comic"'))
    prevSearch = compile(tagre("a", "href", r'(%scomic/[^"]+)' % rurl, after='rel="prev"'))
    adult = True

class TheThinHLine(_TumblrScraper):
    url = 'http://thinhline.tumblr.com/'
    firstStripUrl = url + 'post/4177372348/thl-1-a-cats-got-his-tongue-click-on-the'
    imageSearch = '//img[@id="content-image"]/@data-src'
    prevSearch = '//div[@id="pagination"]/a[text()=">"]'
    latestSearch = '//a[@class="timestamp"]'
    adult = True

    indirectImageSearch = '//div[@id="post"]//a[not(@rel) and img]'

    def getComicStrip(self, url, data):
        """The comic strip image is in a separate page."""
        subPage = self.fetchUrl(url, data, self.indirectImageSearch)
        pageData = self.getPage(subPage)
        return super(TheThinHLine, self).getComicStrip(subPage, pageData)

class TheWhiteboard(_ParserScraper):
    BROKEN_PAGE_MIDDLE = compile(r'\n<')
    url = 'http://www.the-whiteboard.com/'
    stripUrl = url + 'auto%s.html'
    firstStripUrl = stripUrl % 'wb001'
    imageSearch = '//img[contains(@src, "auto")]'
    prevSearch = '//a[.//img[contains(@src, "previous")]]'

    def _parse_page(self, data):
        # Ugly hack to fix broken HTML
        data = self.BROKEN_PAGE_MIDDLE.sub('<', data)
        return super(TheWhiteboard, self)._parse_page(data)

class TheWotch(_WordPressScraper):
    url = 'http://www.thewotch.com/'
    firstStripUrl = url + '?comic=enter-the-wotch'

class ThisIsIndexed(_BasicScraper):
    url = 'http://thisisindexed.com/'
    rurl = escape(url)
    stripUrl = url + 'page/%s'
    imageSearch = compile(tagre("img", "src", r'(%swp-content/uploads/\d+/\d+/card[^"]+)' % rurl))
    multipleImagesPerStrip = True
    prevSearch = compile(tagre("div", "class", "nav-previous") +
                         tagre("a", "href", r'(%spage/\d+/)[^"]*' % rurl))
    help = 'Index format: number'

class ThreePanelSoul(_ComicControlScraper):
    url = 'http://threepanelsoul.com/'
    firstStripUrl = url + 'comic/a-test-comic'

class ToonHole(_WordPressScraper):
    url = 'http://toonhole.com/'
    firstStripUrl = url + 'comic/toon-hole-coming-soon-2010/'

    def shouldSkipUrl(self, url, data):
        return url in (self.url + "comic/if-game-of-thrones-was-animated/",)

class TracesOfThePast(_WPNaviIn):
    baseUrl = 'http://rickgriffinstudios.com/'
    url = baseUrl + 'in-the-new-age/'
    stripUrl = baseUrl + 'comic-post/%s/'
    firstStripUrl = stripUrl % 'totp-page-1'
    latestSearch = '//a[contains(@title, "Permanent Link")]'
    starter = indirectStarter

class TracesOfThePastNSFW(_WPNaviIn):
    name = 'TracesOfThePast/NSFW'
    baseUrl = 'http://rickgriffinstudios.com/'
    url = baseUrl + 'in-the-new-age/'
    stripUrl = baseUrl + 'comic-post/%s/'
    firstStripUrl = stripUrl % 'totp-page-1-nsfw'
    latestSearch = '//a[contains(@title, "NSFW")]'
    starter = indirectStarter
    adult = True

class TracyAndTristan(_BasicScraper):
    url = 'http://tandt.thecomicseries.com/'
    rurl = escape(url)
    stripUrl = url + 'comics/%s'
    imageSearch = compile(tagre("img", "src", r'(%simages/comics/[^"]+)' % rurl))
    prevSearch = compile(tagre("a", "href", r'(/comics/\d+)', after="prev"))
    help = 'Index format: number'

class TrippingOverYou(_BasicScraper):
    url = 'http://www.trippingoveryou.com/'
    stripUrl = url + 'comic/%s'
    firstStripUrl = stripUrl % 'wiggle-room'
    imageSearch = compile(tagre("img", "src", r'([^"]+/comics/[^"]+)'))
    prevSearch = compile(r'')
    help = 'Index format: stripname'

class TumbleDryComics(_WordPressScraper):
    url = 'http://tumbledrycomics.com/'
    stripUrl = url + 'comic/%s/'
    firstStripUrl = stripUrl % 'we-need-to-get-high-jpg'
    textSearch = '//div[@id="comic"]//img/@alt'
    multipleImagesPerStrip = True
    adult = True
    help = 'Index format: name'

    def namer(self, image_url, page_url):
        # Most images have the date they were posted in the filename
        # For those that don't we can get the month and year from the image url
        parts = image_url.rsplit('/', 3)
        year = parts[1]
        month = parts[2]
        filename = parts[3]
        if not filename.startswith(year):
            filename = year + "-" + month + "-" + filename
        return filename

class Turnoff(_ParserScraper):
    name = 'turnoff'
    url = 'https://turnoff.us/'
    imageSearch = '//article[%s]//img' % xpath_class('post-content')
    prevSearch = '//div[%s]//a' % xpath_class('prev')
    stripUrl = url + 'geek/%s'
    firstStripUrl = stripUrl % 'tcp-buddies'
    multipleImagesPerStrip = True

    @cached_property
    def comics_order(self):
        # Neither the images nor the pages contain information about dates or indices.
        # However we can extract the order of the images from the JavaScript.
        html = self.session.get(self.url).text
        list_regex = compile(r"""^\s*"/geek/(.*)",\s*$""", flags=MULTILINE)
        return list(reversed(list_regex.findall(html)))

    def namer(self, image_url, page_url):
        comic_name = page_url.split('/')[-1]
        try:
            index = self.comics_order.index(comic_name) + 1
        except ValueError:
            index = len(self.comics_order)
        file_name = image_url.split('/')[-1]
        return "%03d-%s" % (index, file_name)

class TwoGuysAndGuy(_BasicScraper):
    url = 'http://www.twogag.com/'
    rurl = escape(url)
    stripUrl = url + 'archives/%s'
    firstStripUrl = stripUrl % '4'
    imageSearch = compile(tagre('img', 'src', r'(%scomics/\d{4}-\d{2}-\d{2}[^"]*)' % rurl))
    prevSearch = compile(tagre('a', 'href', r'(%sarchives/\d+)' % rurl,
                               after='title="Previous"'))
    help = 'Index format: number'
    adult = True

class Twokinds(_ParserScraper):
    url = 'http://twokinds.keenspot.com/'
    stripUrl = url + 'comic/%s/'
    firstStripUrl = stripUrl % '1'
    imageSearch = '//article[%s]//img' % xpath_class('comic')
    prevSearch = '//a[%s]' % xpath_class('navprev')
    help = 'Index format: n (unpadded)'

class TwoLumps(_BasicScraper):
    url = 'http://www.twolumps.net/'
    stripUrl = url + 'd/%s.html'
    imageSearch = compile(tagre("img", "src", r'(/comics/[^"]+)'))
    prevSearch = compile(tagre("a", "href", r'(/d/\d+\.html)', after="prev"))
    help = 'Index format: yyyymmdd'
<commit_msg>Add TheOldVictorian<commit_after># -*- coding: utf-8 -*-
# Copyright (C) 2004-2008 Tristan Seligmann and Jonathan Jacobs
# Copyright (C) 2012-2014 Bastian Kleineidam
# Copyright (C) 2015-2017 Tobias Gruetzmacher

from __future__ import absolute_import, division, print_function

from re import compile, escape, MULTILINE
try:
    from functools import cached_property
except ImportError:
    from cached_property import cached_property

from ..scraper import _BasicScraper, _ParserScraper
from ..helpers import indirectStarter, xpath_class
from ..util import tagre
from .common import _ComicControlScraper, _TumblrScraper, _WordPressScraper, _WPNavi, _WPNaviIn

class TalesOfTheQuestor(_ParserScraper):
    stripUrl = 'http://www.rhjunior.com/%s/'
    firstStripUrl = stripUrl % 'tales-of-the-questor-cover'
    url = stripUrl % 'comics/tales-of-the-questor'
    imageSearch = '//div[contains(@class, "entry-content")]//img'
    prevSearch = ('//a[@rel="prev"]', '//a[@title="Tales of the Questor"]')

class TheBrads(_ParserScraper):
    url = 'http://bradcolbow.com/archive/'
    imageSearch = '//div[%s]//img' % xpath_class('entry')
    prevSearch = '//a[%s]' % xpath_class('prev')
    multipleImagesPerStrip = True

class TheClassMenagerie(_ParserScraper):
    stripUrl = 'http://www.theclassm.com/d/%s.html'
    url = stripUrl % '20050717'
    firstStripUrl = stripUrl % '19990322'
    imageSearch = '//img[@class="ksc"]'
    prevSearch = '//a[@rel="prev"]'
    multipleImagesPerStrip = True
    endOfLife = True

class TheDevilsPanties(_WPNavi):
    url = 'http://thedevilspanties.com/'
    stripUrl = url + 'archives/%s'
    firstStripUrl = stripUrl % '300'
    help = 'Index format: number'

class TheDreamlandChronicles(_WordPressScraper):
    url = 'http://www.thedreamlandchronicles.com/'

class TheGamerCat(_ParserScraper):
    url = 'https://thegamercat.com/'
    stripUrl = url + 'comic/%s/'
    firstStripUrl = stripUrl % '06102011'
    imageSearch = '//div[@id="comic"]//img'
    prevSearch = '//a[contains(@class, "comic-nav-previous")]'
    help = 'Index format: stripname'

class TheGentlemansArmchair(_WordPressScraper):
    url = 'http://thegentlemansarmchair.com/'

class TheLandscaper(_BasicScraper):
    stripUrl = 'http://landscaper.visual-assault.net/comic/%s'
    url = stripUrl % 'latest'
    firstStripUrl = stripUrl % '1'
    imageSearch = compile(tagre("img", "src",
                                r'(/comics/comic/comic_page/[^"]+)'))
    prevSearch = compile(tagre("a", "href", r'(/comic/[^"]+)') +
                         '‹ Previous')
    help = 'Index format: name'

class TheMelvinChronicles(_WordPressScraper):
    url = 'http://melvin.jeaniebottle.com/'

class TheNoob(_WordPressScraper):
    url = 'http://thenoobcomic.com/'
    stripUrl = url + 'comic/%s/'
    firstStripUrl = stripUrl % '1'
    help = 'Index format: n (unpadded)'

class TheOldVictorian(_ParserScraper):
    url = 'http://theoldvictorianwebcomic.com/'
    stripUrl = url + 'comic/%s/'
    firstStripUrl = stripUrl % 'the-old-victorian-cover'
    imageSearch = '//div[@id="comic"]//img'
    prevSearch = '//a[contains(@class, "comic-nav-previous")]'

    def namer(self, imageUrl, pageUrl):
        filename = imageUrl.rsplit('/', 1)[-1].replace('_', '-')
        filename = filename.replace('TOV00', 'TOV-00')
        if filename.replace('oldvic', '')[0].isdigit():
            filename = filename.replace('oldvic', 'TOV-00')
        if 'TOV-000' in filename and len(filename) > 12:
            filename = filename[:8] + '-' + filename[8:]
        return filename

class TheOrderOfTheStick(_BasicScraper):
    url = 'http://www.giantitp.com/'
    stripUrl = url + 'comics/oots%s.html'
    firstStripUrl = stripUrl % '0001'
    imageSearch = compile(r'')
    prevSearch = compile(r'<IMG src="/Images/redesign/ComicNav_Back.gif"')
    latestSearch = compile(r'<A href="(/comics/oots\d{4}\.html)"')
    help = 'Index format: n (unpadded)'
    starter = indirectStarter

    def namer(self, image_url, page_url):
        return page_url.rsplit('/', 1)[-1][:-5]

class TheRockCocks(_BasicScraper):
    url = 'http://rockcocks.slipshine.net/'
    rurl = escape(url)
    stripUrl = url + 'comics/%s'
    firstStripUrl = stripUrl % "page-1-nsfw-track-1-start"
    imageSearch = compile(tagre("img", "src", r'(%scomics/[^"]+)' % rurl, after='id="cc-comic"'))
    prevSearch = compile(tagre("a", "href", r'(%scomic/[^"]+)' % rurl, after='rel="prev"'))
    adult = True

class TheThinHLine(_TumblrScraper):
    url = 'http://thinhline.tumblr.com/'
    firstStripUrl = url + 'post/4177372348/thl-1-a-cats-got-his-tongue-click-on-the'
    imageSearch = '//img[@id="content-image"]/@data-src'
    prevSearch = '//div[@id="pagination"]/a[text()=">"]'
    latestSearch = '//a[@class="timestamp"]'
    adult = True

    indirectImageSearch = '//div[@id="post"]//a[not(@rel) and img]'

    def getComicStrip(self, url, data):
        """The comic strip image is in a separate page."""
        subPage = self.fetchUrl(url, data, self.indirectImageSearch)
        pageData = self.getPage(subPage)
        return super(TheThinHLine, self).getComicStrip(subPage, pageData)

class TheWhiteboard(_ParserScraper):
    BROKEN_PAGE_MIDDLE = compile(r'\n<')
    url = 'http://www.the-whiteboard.com/'
    stripUrl = url + 'auto%s.html'
    firstStripUrl = stripUrl % 'wb001'
    imageSearch = '//img[contains(@src, "auto")]'
    prevSearch = '//a[.//img[contains(@src, "previous")]]'

    def _parse_page(self, data):
        # Ugly hack to fix broken HTML
        data = self.BROKEN_PAGE_MIDDLE.sub('<', data)
        return super(TheWhiteboard, self)._parse_page(data)

class TheWotch(_WordPressScraper):
    url = 'http://www.thewotch.com/'
    firstStripUrl = url + '?comic=enter-the-wotch'

class ThisIsIndexed(_BasicScraper):
    url = 'http://thisisindexed.com/'
    rurl = escape(url)
    stripUrl = url + 'page/%s'
    imageSearch = compile(tagre("img", "src", r'(%swp-content/uploads/\d+/\d+/card[^"]+)' % rurl))
    multipleImagesPerStrip = True
    prevSearch = compile(tagre("div", "class", "nav-previous") +
                         tagre("a", "href", r'(%spage/\d+/)[^"]*' % rurl))
    help = 'Index format: number'

class ThreePanelSoul(_ComicControlScraper):
    url = 'http://threepanelsoul.com/'
    firstStripUrl = url + 'comic/a-test-comic'

class ToonHole(_WordPressScraper):
    url = 'http://toonhole.com/'
    firstStripUrl = url + 'comic/toon-hole-coming-soon-2010/'

    def shouldSkipUrl(self, url, data):
        return url in (self.url + "comic/if-game-of-thrones-was-animated/",)

class TracesOfThePast(_WPNaviIn):
    baseUrl = 'http://rickgriffinstudios.com/'
    url = baseUrl + 'in-the-new-age/'
    stripUrl = baseUrl + 'comic-post/%s/'
    firstStripUrl = stripUrl % 'totp-page-1'
    latestSearch = '//a[contains(@title, "Permanent Link")]'
    starter = indirectStarter

class TracesOfThePastNSFW(_WPNaviIn):
    name = 'TracesOfThePast/NSFW'
    baseUrl = 'http://rickgriffinstudios.com/'
    url = baseUrl + 'in-the-new-age/'
    stripUrl = baseUrl + 'comic-post/%s/'
    firstStripUrl = stripUrl % 'totp-page-1-nsfw'
    latestSearch = '//a[contains(@title, "NSFW")]'
    starter = indirectStarter
    adult = True

class TracyAndTristan(_BasicScraper):
    url = 'http://tandt.thecomicseries.com/'
    rurl = escape(url)
    stripUrl = url + 'comics/%s'
    imageSearch = compile(tagre("img", "src", r'(%simages/comics/[^"]+)' % rurl))
    prevSearch = compile(tagre("a", "href", r'(/comics/\d+)', after="prev"))
    help = 'Index format: number'

class TrippingOverYou(_BasicScraper):
    url = 'http://www.trippingoveryou.com/'
    stripUrl = url + 'comic/%s'
    firstStripUrl = stripUrl % 'wiggle-room'
    imageSearch = compile(tagre("img", "src", r'([^"]+/comics/[^"]+)'))
    prevSearch = compile(r'')
    help = 'Index format: stripname'

class TumbleDryComics(_WordPressScraper):
    url = 'http://tumbledrycomics.com/'
    stripUrl = url + 'comic/%s/'
    firstStripUrl = stripUrl % 'we-need-to-get-high-jpg'
    textSearch = '//div[@id="comic"]//img/@alt'
    multipleImagesPerStrip = True
    adult = True
    help = 'Index format: name'

    def namer(self, image_url, page_url):
        # Most images have the date they were posted in the filename
        # For those that don't we can get the month and year from the image url
        parts = image_url.rsplit('/', 3)
        year = parts[1]
        month = parts[2]
        filename = parts[3]
        if not filename.startswith(year):
            filename = year + "-" + month + "-" + filename
        return filename

class Turnoff(_ParserScraper):
    name = 'turnoff'
    url = 'https://turnoff.us/'
    imageSearch = '//article[%s]//img' % xpath_class('post-content')
    prevSearch = '//div[%s]//a' % xpath_class('prev')
    stripUrl = url + 'geek/%s'
    firstStripUrl = stripUrl % 'tcp-buddies'
    multipleImagesPerStrip = True

    @cached_property
    def comics_order(self):
        # Neither the images nor the pages contain information about dates or indices.
        # However we can extract the order of the images from the JavaScript.
        html = self.session.get(self.url).text
        list_regex = compile(r"""^\s*"/geek/(.*)",\s*$""", flags=MULTILINE)
        return list(reversed(list_regex.findall(html)))

    def namer(self, image_url, page_url):
        comic_name = page_url.split('/')[-1]
        try:
            index = self.comics_order.index(comic_name) + 1
        except ValueError:
            index = len(self.comics_order)
        file_name = image_url.split('/')[-1]
        return "%03d-%s" % (index, file_name)

class TwoGuysAndGuy(_BasicScraper):
    url = 'http://www.twogag.com/'
    rurl = escape(url)
    stripUrl = url + 'archives/%s'
    firstStripUrl = stripUrl % '4'
    imageSearch = compile(tagre('img', 'src', r'(%scomics/\d{4}-\d{2}-\d{2}[^"]*)' % rurl))
    prevSearch = compile(tagre('a', 'href', r'(%sarchives/\d+)' % rurl,
                               after='title="Previous"'))
    help = 'Index format: number'
    adult = True

class Twokinds(_ParserScraper):
    url = 'http://twokinds.keenspot.com/'
    stripUrl = url + 'comic/%s/'
    firstStripUrl = stripUrl % '1'
    imageSearch = '//article[%s]//img' % xpath_class('comic')
    prevSearch = '//a[%s]' % xpath_class('navprev')
    help = 'Index format: n (unpadded)'

class TwoLumps(_BasicScraper):
    url = 'http://www.twolumps.net/'
    stripUrl = url + 'd/%s.html'
    imageSearch = compile(tagre("img", "src", r'(/comics/[^"]+)'))
    prevSearch = compile(tagre("a", "href", r'(/d/\d+\.html)', after="prev"))
    help = 'Index format: yyyymmdd'
<|endoftext|>


<commit_before>""" JSON Web Token implementation

Minimum implementation based on this spec:
http://self-issued.info/docs/draft-jones-json-web-token-01.html
"""
from __future__ import unicode_literals
import base64
import binascii
import hashlib
import hmac
import sys

from datetime import datetime
from calendar import timegm
from collections import Mapping

try:
    import json
except ImportError:
    import simplejson as json

if sys.version_info >= (3, 0, 0):
    unicode = str
    basestring = str

__version__ = '0.3.0'
__all__ = ['encode', 'decode', 'DecodeError']

class DecodeError(Exception):
    pass

class ExpiredSignature(Exception):
    pass

class InvalidAudience(Exception):
    pass

class InvalidIssuer(Exception):
    pass

signing_methods = {
    'none': lambda msg, key: b'',
    'HS256': lambda msg, key: hmac.new(key, msg, hashlib.sha256).digest(),
    'HS384': lambda msg, key: hmac.new(key, msg, hashlib.sha384).digest(),
    'HS512': lambda msg, key: hmac.new(key, msg, hashlib.sha512).digest()
}

verify_methods = {
    'HS256': lambda msg, key: hmac.new(key, msg, hashlib.sha256).digest(),
    'HS384': lambda msg, key: hmac.new(key, msg, hashlib.sha384).digest(),
    'HS512': lambda msg, key: hmac.new(key, msg, hashlib.sha512).digest()
}

def prepare_HS_key(key):
    if not isinstance(key, basestring) and not isinstance(key, bytes):
        raise TypeError('Expecting a string- or bytes-formatted key.')

    if isinstance(key, unicode):
        key = key.encode('utf-8')

    return key

prepare_key_methods = {
    'none': lambda key: None,
    'HS256': prepare_HS_key,
    'HS384': prepare_HS_key,
    'HS512': prepare_HS_key
}

try:
    from Crypto.Signature import PKCS1_v1_5
    from Crypto.Hash import SHA256
    from Crypto.Hash import SHA384
    from Crypto.Hash import SHA512
    from Crypto.PublicKey import RSA

    signing_methods.update({
        'RS256': lambda msg, key: PKCS1_v1_5.new(key).sign(SHA256.new(msg)),
        'RS384': lambda msg, key: PKCS1_v1_5.new(key).sign(SHA384.new(msg)),
        'RS512': lambda msg, key: PKCS1_v1_5.new(key).sign(SHA512.new(msg))
    })

    verify_methods.update({
        'RS256': lambda msg, key, sig: PKCS1_v1_5.new(key).verify(SHA256.new(msg), sig),
        'RS384': lambda msg, key, sig: PKCS1_v1_5.new(key).verify(SHA384.new(msg), sig),
        'RS512': lambda msg, key, sig: PKCS1_v1_5.new(key).verify(SHA512.new(msg), sig)
    })

    def prepare_RS_key(key):
        if isinstance(key, RSA._RSAobj):
            return key

        if isinstance(key, basestring):
            if isinstance(key, unicode):
                key = key.encode('utf-8')

            key = RSA.importKey(key)
        else:
            raise TypeError('Expecting a PEM- or RSA-formatted key.')

        return key

    prepare_key_methods.update({
        'RS256': prepare_RS_key,
        'RS384': prepare_RS_key,
        'RS512': prepare_RS_key
    })

except ImportError:
    pass

try:
    import ecdsa
    from Crypto.Hash import SHA256
    from Crypto.Hash import SHA384
    from Crypto.Hash import SHA512

    signing_methods.update({
        'ES256': lambda msg, key: key.sign(msg, hashfunc=hashlib.sha256, sigencode=ecdsa.util.sigencode_der),
        'ES384': lambda msg, key: key.sign(msg, hashfunc=hashlib.sha384, sigencode=ecdsa.util.sigencode_der),
        'ES512': lambda msg, key: key.sign(msg, hashfunc=hashlib.sha512, sigencode=ecdsa.util.sigencode_der),
    })

    verify_methods.update({
        'ES256': lambda msg, key, sig: key.verify(sig, msg, hashfunc=hashlib.sha256, sigdecode=ecdsa.util.sigdecode_der),
        'ES384': lambda msg, key, sig: key.verify(sig, msg, hashfunc=hashlib.sha384, sigdecode=ecdsa.util.sigdecode_der),
        'ES512': lambda msg, key, sig: key.verify(sig, msg, hashfunc=hashlib.sha512, sigdecode=ecdsa.util.sigdecode_der),
    })

    def prepare_ES_key(key):
        if isinstance(key, ecdsa.SigningKey) or \
           isinstance(key, ecdsa.VerifyingKey):
            return key

        if isinstance(key, basestring):
            if isinstance(key, unicode):
                key = key.encode('utf-8')

            # Attempt to load key. We don't know if it's
            # a Signing Key or a Verifying Key, so we try
            # the Verifying Key first.
            try:
                key = ecdsa.VerifyingKey.from_pem(key)
            except ecdsa.der.UnexpectedDER:
                try:
                    key = ecdsa.SigningKey.from_pem(key)
                except:
                    raise
        else:
            raise TypeError('Expecting a PEM-formatted key.')

        return key

    prepare_key_methods.update({
        'ES256': prepare_ES_key,
        'ES384': prepare_ES_key,
        'ES512': prepare_ES_key
    })

except ImportError:
    pass

def constant_time_compare(val1, val2):
    """
    Returns True if the two strings are equal, False otherwise.

    The time taken is independent of the number of characters that match.
    """
    if len(val1) != len(val2):
        return False

    result = 0

    if sys.version_info >= (3, 0, 0):
        # Bytes are numbers
        for x, y in zip(val1, val2):
            result |= x ^ y
    else:
        for x, y in zip(val1, val2):
            result |= ord(x) ^ ord(y)

    return result == 0

def base64url_decode(input):
    rem = len(input) % 4

    if rem > 0:
        input += b'=' * (4 - rem)

    return base64.urlsafe_b64decode(input)

def base64url_encode(input):
    return base64.urlsafe_b64encode(input).replace(b'=', b'')

def header(jwt):
    if isinstance(jwt, unicode):
        jwt = jwt.encode('utf-8')
    header_segment = jwt.split(b'.', 1)[0]
    try:
        header_data = base64url_decode(header_segment)
        return json.loads(header_data.decode('utf-8'))
    except (ValueError, TypeError):
        raise DecodeError('Invalid header encoding')

def encode(payload, key, algorithm='HS256', headers=None):
    segments = []

    if algorithm is None:
        algorithm = 'none'

    # Check that we get a mapping
    if not isinstance(payload, Mapping):
        raise TypeError('Expecting a mapping object, as json web token only'
                        'support json objects.')

    # Header
    header = {'typ': 'JWT', 'alg': algorithm}
    if headers:
        header.update(headers)

    json_header = json.dumps(header, separators=(',', ':')).encode('utf-8')
    segments.append(base64url_encode(json_header))

    # Payload
    for time_claim in ['exp', 'iat', 'nbf']:
        # Convert datetime to a intDate value in known time-format claims
        if isinstance(payload.get(time_claim), datetime):
            payload[time_claim] = timegm(payload[time_claim].utctimetuple())

    json_payload = json.dumps(payload, separators=(',', ':')).encode('utf-8')
    segments.append(base64url_encode(json_payload))

    # Segments
    signing_input = b'.'.join(segments)
    try:
        key = prepare_key_methods[algorithm](key)
        signature = signing_methods[algorithm](signing_input, key)
    except KeyError:
        raise NotImplementedError('Algorithm not supported')

    segments.append(base64url_encode(signature))

    return b'.'.join(segments)

def decode(jwt, key='', verify=True, **kwargs):
    payload, signing_input, header, signature = load(jwt)

    if verify:
        verify_expiration = kwargs.pop('verify_expiration', True)
        leeway = kwargs.pop('leeway', 0)

        verify_signature(payload, signing_input, header, signature, key,
                         verify_expiration, leeway, **kwargs)

    return payload

def load(jwt):
    if isinstance(jwt, unicode):
        jwt = jwt.encode('utf-8')
    try:
        signing_input, crypto_segment = jwt.rsplit(b'.', 1)
        header_segment, payload_segment = signing_input.split(b'.', 1)
    except ValueError:
        raise DecodeError('Not enough segments')

    try:
        header_data = base64url_decode(header_segment)
    except (TypeError, binascii.Error):
        raise DecodeError('Invalid header padding')
    try:
        header = json.loads(header_data.decode('utf-8'))
    except ValueError as e:
        raise DecodeError('Invalid header string: %s' % e)

    try:
        payload_data = base64url_decode(payload_segment)
    except (TypeError, binascii.Error):
        raise DecodeError('Invalid payload padding')
    try:
        payload = json.loads(payload_data.decode('utf-8'))
    except ValueError as e:
        raise DecodeError('Invalid payload string: %s' % e)

    try:
        signature = base64url_decode(crypto_segment)
    except (TypeError, binascii.Error):
        raise DecodeError('Invalid crypto padding')

    return (payload, signing_input, header, signature)

def verify_signature(payload, signing_input, header, signature, key='',
                     verify_expiration=True, leeway=0, **kwargs):
    try:
        algorithm = header['alg'].upper()
        key = prepare_key_methods[algorithm](key)

        if algorithm.startswith('HS'):
            expected = verify_methods[algorithm](signing_input, key)

            if not constant_time_compare(signature, expected):
                raise DecodeError('Signature verification failed')
        else:
            if not verify_methods[algorithm](signing_input, key, signature):
                raise DecodeError('Signature verification failed')
    except KeyError:
        raise DecodeError('Algorithm not supported')

    if 'nbf' in payload and verify_expiration:
        utc_timestamp = timegm(datetime.utcnow().utctimetuple())

        if payload['nbf'] > (utc_timestamp + leeway):
            raise ExpiredSignature('Signature not yet valid')

    if 'exp' in payload and verify_expiration:
        utc_timestamp = timegm(datetime.utcnow().utctimetuple())

        if payload['exp'] < (utc_timestamp - leeway):
            raise ExpiredSignature('Signature has expired')

    audience = kwargs.get('audience')

    if audience:
        if isinstance(audience, list):
            audiences = audience
        else:
            audiences = [audience]

        if payload.get('aud') not in audiences:
            raise InvalidAudience('Invalid audience')

    issuer = kwargs.get('issuer')

    if issuer:
        if payload.get('iss') != issuer:
            raise InvalidIssuer('Invalid issuer')
<commit_msg>Bump up version for release<commit_after>""" JSON Web Token implementation

Minimum implementation based on this spec:
http://self-issued.info/docs/draft-jones-json-web-token-01.html
"""
from __future__ import unicode_literals
import base64
import binascii
import hashlib
import hmac
import sys

from datetime import datetime
from calendar import timegm
from collections import Mapping

try:
    import json
except ImportError:
    import simplejson as json

if sys.version_info >= (3, 0, 0):
    unicode = str
    basestring = str

__version__ = '0.3.1'
__all__ = ['encode', 'decode', 'DecodeError']

class DecodeError(Exception):
    pass

class ExpiredSignature(Exception):
    pass

class InvalidAudience(Exception):
    pass

class InvalidIssuer(Exception):
    pass

signing_methods = {
    'none': lambda msg, key: b'',
    'HS256': lambda msg, key: hmac.new(key, msg, hashlib.sha256).digest(),
    'HS384': lambda msg, key: hmac.new(key, msg, hashlib.sha384).digest(),
    'HS512': lambda msg, key: hmac.new(key, msg, hashlib.sha512).digest()
}

verify_methods = {
    'HS256': lambda msg, key: hmac.new(key, msg, hashlib.sha256).digest(),
    'HS384': lambda msg, key: hmac.new(key, msg, hashlib.sha384).digest(),
    'HS512': lambda msg, key: hmac.new(key, msg, hashlib.sha512).digest()
}

def prepare_HS_key(key):
    if not isinstance(key, basestring) and not isinstance(key, bytes):
        raise TypeError('Expecting a string- or bytes-formatted key.')

    if isinstance(key, unicode):
        key = key.encode('utf-8')

    return key

prepare_key_methods = {
    'none': lambda key: None,
    'HS256': prepare_HS_key,
    'HS384': prepare_HS_key,
    'HS512': prepare_HS_key
}

try:
    from Crypto.Signature import PKCS1_v1_5
    from Crypto.Hash import SHA256
    from Crypto.Hash import SHA384
    from Crypto.Hash import SHA512
    from Crypto.PublicKey import RSA

    signing_methods.update({
        'RS256': lambda msg, key: PKCS1_v1_5.new(key).sign(SHA256.new(msg)),
        'RS384': lambda msg, key: PKCS1_v1_5.new(key).sign(SHA384.new(msg)),
        'RS512': lambda msg, key: PKCS1_v1_5.new(key).sign(SHA512.new(msg))
    })

    verify_methods.update({
        'RS256': lambda msg, key, sig: PKCS1_v1_5.new(key).verify(SHA256.new(msg), sig),
        'RS384': lambda msg, key, sig: PKCS1_v1_5.new(key).verify(SHA384.new(msg), sig),
        'RS512': lambda msg, key, sig: PKCS1_v1_5.new(key).verify(SHA512.new(msg), sig)
    })

    def prepare_RS_key(key):
        if isinstance(key, RSA._RSAobj):
            return key

        if isinstance(key, basestring):
            if isinstance(key, unicode):
                key = key.encode('utf-8')

            key = RSA.importKey(key)
        else:
            raise TypeError('Expecting a PEM- or RSA-formatted key.')

        return key

    prepare_key_methods.update({
        'RS256': prepare_RS_key,
        'RS384': prepare_RS_key,
        'RS512': prepare_RS_key
    })

except ImportError:
    pass

try:
    import ecdsa
    from Crypto.Hash import SHA256
    from Crypto.Hash import SHA384
    from Crypto.Hash import SHA512

    signing_methods.update({
        'ES256': lambda msg, key: key.sign(msg, hashfunc=hashlib.sha256, sigencode=ecdsa.util.sigencode_der),
        'ES384': lambda msg, key: key.sign(msg, hashfunc=hashlib.sha384, sigencode=ecdsa.util.sigencode_der),
        'ES512': lambda msg, key: key.sign(msg, hashfunc=hashlib.sha512, sigencode=ecdsa.util.sigencode_der),
    })

    verify_methods.update({
        'ES256': lambda msg, key, sig: key.verify(sig, msg, hashfunc=hashlib.sha256, sigdecode=ecdsa.util.sigdecode_der),
        'ES384': lambda msg, key, sig: key.verify(sig, msg, hashfunc=hashlib.sha384, sigdecode=ecdsa.util.sigdecode_der),
        'ES512': lambda msg, key, sig: key.verify(sig, msg, hashfunc=hashlib.sha512, sigdecode=ecdsa.util.sigdecode_der),
    })

    def prepare_ES_key(key):
        if isinstance(key, ecdsa.SigningKey) or \
           isinstance(key, ecdsa.VerifyingKey):
            return key

        if isinstance(key, basestring):
            if isinstance(key, unicode):
                key = key.encode('utf-8')

            # Attempt to load key. We don't know if it's
            # a Signing Key or a Verifying Key, so we try
            # the Verifying Key first.
            try:
                key = ecdsa.VerifyingKey.from_pem(key)
            except ecdsa.der.UnexpectedDER:
                try:
                    key = ecdsa.SigningKey.from_pem(key)
                except:
                    raise
        else:
            raise TypeError('Expecting a PEM-formatted key.')

        return key

    prepare_key_methods.update({
        'ES256': prepare_ES_key,
        'ES384': prepare_ES_key,
        'ES512': prepare_ES_key
    })

except ImportError:
    pass

def constant_time_compare(val1, val2):
    """
    Returns True if the two strings are equal, False otherwise.

    The time taken is independent of the number of characters that match.
    """
    if len(val1) != len(val2):
        return False

    result = 0

    if sys.version_info >= (3, 0, 0):
        # Bytes are numbers
        for x, y in zip(val1, val2):
            result |= x ^ y
    else:
        for x, y in zip(val1, val2):
            result |= ord(x) ^ ord(y)

    return result == 0

def base64url_decode(input):
    rem = len(input) % 4

    if rem > 0:
        input += b'=' * (4 - rem)

    return base64.urlsafe_b64decode(input)

def base64url_encode(input):
    return base64.urlsafe_b64encode(input).replace(b'=', b'')

def header(jwt):
    if isinstance(jwt, unicode):
        jwt = jwt.encode('utf-8')
    header_segment = jwt.split(b'.', 1)[0]
    try:
        header_data = base64url_decode(header_segment)
        return json.loads(header_data.decode('utf-8'))
    except (ValueError, TypeError):
        raise DecodeError('Invalid header encoding')

def encode(payload, key, algorithm='HS256', headers=None):
    segments = []

    if algorithm is None:
        algorithm = 'none'

    # Check that we get a mapping
    if not isinstance(payload, Mapping):
        raise TypeError('Expecting a mapping object, as json web token only'
                        'support json objects.')

    # Header
    header = {'typ': 'JWT', 'alg': algorithm}
    if headers:
        header.update(headers)

    json_header = json.dumps(header, separators=(',', ':')).encode('utf-8')
    segments.append(base64url_encode(json_header))

    # Payload
    for time_claim in ['exp', 'iat', 'nbf']:
        # Convert datetime to a intDate value in known time-format claims
        if isinstance(payload.get(time_claim), datetime):
            payload[time_claim] = timegm(payload[time_claim].utctimetuple())

    json_payload = json.dumps(payload, separators=(',', ':')).encode('utf-8')
    segments.append(base64url_encode(json_payload))

    # Segments
    signing_input = b'.'.join(segments)
    try:
        key = prepare_key_methods[algorithm](key)
        signature = signing_methods[algorithm](signing_input, key)
    except KeyError:
        raise NotImplementedError('Algorithm not supported')

    segments.append(base64url_encode(signature))

    return b'.'.join(segments)

def decode(jwt, key='', verify=True, **kwargs):
    payload, signing_input, header, signature = load(jwt)

    if verify:
        verify_expiration = kwargs.pop('verify_expiration', True)
        leeway = kwargs.pop('leeway', 0)

        verify_signature(payload, signing_input, header, signature, key,
                         verify_expiration, leeway, **kwargs)

    return payload

def load(jwt):
    if isinstance(jwt, unicode):
        jwt = jwt.encode('utf-8')
    try:
        signing_input, crypto_segment = jwt.rsplit(b'.', 1)
        header_segment, payload_segment = signing_input.split(b'.', 1)
    except ValueError:
        raise DecodeError('Not enough segments')

    try:
        header_data = base64url_decode(header_segment)
    except (TypeError, binascii.Error):
        raise DecodeError('Invalid header padding')
    try:
        header = json.loads(header_data.decode('utf-8'))
    except ValueError as e:
        raise DecodeError('Invalid header string: %s' % e)

    try:
        payload_data = base64url_decode(payload_segment)
    except (TypeError, binascii.Error):
        raise DecodeError('Invalid payload padding')
    try:
        payload = json.loads(payload_data.decode('utf-8'))
    except ValueError as e:
        raise DecodeError('Invalid payload string: %s' % e)

    try:
        signature = base64url_decode(crypto_segment)
    except (TypeError, binascii.Error):
        raise DecodeError('Invalid crypto padding')

    return (payload, signing_input, header, signature)

def verify_signature(payload, signing_input, header, signature, key='',
                     verify_expiration=True, leeway=0, **kwargs):
    try:
        algorithm = header['alg'].upper()
        key = prepare_key_methods[algorithm](key)

        if algorithm.startswith('HS'):
            expected = verify_methods[algorithm](signing_input, key)

            if not constant_time_compare(signature, expected):
                raise DecodeError('Signature verification failed')
        else:
            if not verify_methods[algorithm](signing_input, key, signature):
                raise DecodeError('Signature verification failed')
    except KeyError:
        raise DecodeError('Algorithm not supported')

    if 'nbf' in payload and verify_expiration:
        utc_timestamp = timegm(datetime.utcnow().utctimetuple())

        if payload['nbf'] > (utc_timestamp + leeway):
            raise ExpiredSignature('Signature not yet valid')

    if 'exp' in payload and verify_expiration:
        utc_timestamp = timegm(datetime.utcnow().utctimetuple())

        if payload['exp'] < (utc_timestamp - leeway):
            raise ExpiredSignature('Signature has expired')

    audience = kwargs.get('audience')

    if audience:
        if isinstance(audience, list):
            audiences = audience
        else:
            audiences = [audience]

        if payload.get('aud') not in audiences:
            raise InvalidAudience('Invalid audience')

    issuer = kwargs.get('issuer')

    if issuer:
        if payload.get('iss') != issuer:
            raise InvalidIssuer('Invalid issuer')
<|endoftext|>


<commit_before># -*- coding: iso-8859-1 -*-
# Copyright (C) 2004-2005 Tristan Seligmann and Jonathan Jacobs
# Copyright (C) 2012-2014 Bastian Kleineidam

from re import compile
from ..scraper import _BasicScraper
from ..util import tagre

class YAFGC(_BasicScraper):
    description = u'Yet Another Fantasy Gamer Comic'
    url = 'http://yafgc.net/'
    stripUrl = url + '?id=%s'
    firstStripUrl = stripUrl % '1'
    imageSearch = compile(tagre("img", "src", r'(http://(?:www\.)?yafgc\.net/img/comic/\d+\.jpg)'))
    prevSearch = compile(tagre("a", "href", r'(http://(?:www\.)?yafgc\.net/\?id=\d+)') +
      tagre("img", "src", r'/img/navbar/go_to_previous\.gif'))
    help = 'Index format: number'

    def prevUrlModifier(cls, prevUrl):
        if prevUrl:
            return prevUrl.replace("www.yafgc.net", "yafgc.net")
<commit_msg>Add missing @classmethod<commit_after># -*- coding: iso-8859-1 -*-
# Copyright (C) 2004-2005 Tristan Seligmann and Jonathan Jacobs
# Copyright (C) 2012-2014 Bastian Kleineidam

from re import compile
from ..scraper import _BasicScraper
from ..util import tagre

class YAFGC(_BasicScraper):
    description = u'Yet Another Fantasy Gamer Comic'
    url = 'http://yafgc.net/'
    stripUrl = url + '?id=%s'
    firstStripUrl = stripUrl % '1'
    imageSearch = compile(tagre("img", "src", r'(http://(?:www\.)?yafgc\.net/img/comic/\d+\.jpg)'))
    prevSearch = compile(tagre("a", "href", r'(http://(?:www\.)?yafgc\.net/\?id=\d+)') +
      tagre("img", "src", r'/img/navbar/go_to_previous\.gif'))
    help = 'Index format: number'

    @classmethod
    def prevUrlModifier(cls, prevUrl):
        if prevUrl:
            return prevUrl.replace("www.yafgc.net", "yafgc.net")
<|endoftext|>


<commit_before>from __future__ import division, print_function
import numpy as np
from numpy import pi
import pandas as pd

FEEDBACK_ON    =  1
FEEDBACK_OFF   =  0
FEEDBACK_FALSE = -1

def f(x, t):
    return pd.Series((x[0] * 2*pi*x[1] / 240.) * np.cos(2*pi*x[1] * t / 240. + x[3]))

def rescale(x):
    ''' Rescale array to [-1, 1] '''
    x -= min(x)
    x /= 0.5 * max(abs(x))
    return x - 1

def generate_path(trial_number, length=240, fps=60):
    # Sweet, Barbara Townsend, and Leonard J. Trejo.
    # "The identification and modeling of visual cue
    # usage in manual control task experiments." (1999).
    data = [[.50,   6,  0.16],
            [.50,  10,  0.26],
            [.50,  15,  0.39],
            [.50,  23,  0.60],
            [.50,  37,  0.97],
            [.50,  59,  1.54],
            [.05, 101,  2.64],
            [.05, 127,  3.32],
            [.05, 149,  3.90],
            [.05, 179,  4.69],
            [.05, 311,  8.14],
            [.05, 521, 13.64]]

    np.random.seed(trial_number)
    d = pd.DataFrame(data)

    offsets = np.random.uniform(-pi, pi, (100, 12))
    d[3] = offsets[trial_number]

    t = np.linspace(0, length, length * fps)
    sines = d[:-4].apply(f, args=(t,), axis=1).T
    sines.index = t
    sines = sines.sum(axis=1)
    sines = rescale(sines).tolist()

    return sines

ks = [# Training
      {'trial': 1,
       'rand_id': 1,
       'feedback': FEEDBACK_ON,
       'secondary_task': False},
      {'trial': 2,
       'rand_id': 2,
       'feedback': FEEDBACK_ON,
       'secondary_task': False},
      {'trial': 3,
       'rand_id': 3,
       'feedback': FEEDBACK_ON,
       'secondary_task': False},
      {'trial': 4,
       'rand_id': 4,
       'feedback': FEEDBACK_ON,
       'secondary_task': False},
      {'trial': 5,
       'rand_id': 5,
       'feedback': FEEDBACK_ON,
       'secondary_task': False},
      {'trial': 6,
       'rand_id': 1,
       'feedback': FEEDBACK_ON,
       'secondary_task': True},
      {'trial': 7,
       'rand_id': 2,
       'feedback': FEEDBACK_ON,
       'secondary_task': True},
      {'trial': 8,
       'rand_id': 3,
       'feedback': FEEDBACK_ON,
       'secondary_task': True},
      {'trial': 9,
       'rand_id': 4,
       'feedback': FEEDBACK_ON,
       'secondary_task': True},
      {'trial': 10,
       'rand_id': 5,
       'feedback': FEEDBACK_ON,
       'secondary_task': True},

      # Experiment
      {'trial': 11,  # 28
       'rand_id': 10,
       'feedback': FEEDBACK_ON,
       'secondary_task': False},
      {'trial': 12,  # 14
       'rand_id': 11,
       'feedback': FEEDBACK_ON,
       'secondary_task': False},
      {'trial': 13,  # 19
       'rand_id': 10,
       'feedback': FEEDBACK_ON,
       'secondary_task': True},
      {'trial': 14,  # 15
       'rand_id': 11,
       'feedback': FEEDBACK_ON,
       'secondary_task': True},
      {'trial': 15,  # 24
       'rand_id': 12,
       'feedback': FEEDBACK_ON,
       'secondary_task': False},
      {'trial': 16,  # 20
       'rand_id': 13,
       'feedback': FEEDBACK_ON,
       'secondary_task': False},
      {'trial': 17,  # 11
       'rand_id': 12,
       'feedback': FEEDBACK_ON,
       'secondary_task': True},
      {'trial': 18,  # 12
       'rand_id': 14,
       'feedback': FEEDBACK_ON,
       'secondary_task': False},
      {'trial': 19,  # 23
       'rand_id': 13,
       'feedback': FEEDBACK_ON,
       'secondary_task': True},
      {'trial': 20,  # 22
       'rand_id': 15,
       'feedback': FEEDBACK_ON,
       'secondary_task': False},
      {'trial': 21,  # 25
       'rand_id': 14,
       'feedback': FEEDBACK_ON,
       'secondary_task': True},
      {'trial': 22,  # 13
       'rand_id': 15,
       'feedback': FEEDBACK_ON,
       'secondary_task': True},
      {'trial': 23,  # 18
       'rand_id': 16,
       'feedback': FEEDBACK_ON,
       'secondary_task': False},
      {'trial': 24,  # 27
       'rand_id': 16,
       'feedback': FEEDBACK_ON,
       'secondary_task': True},
      {'trial': 25,  # 26
       'rand_id': 17,
       'feedback': FEEDBACK_ON,
       'secondary_task': False},
      {'trial': 26,  # 17
       'rand_id': 17,
       'feedback': FEEDBACK_ON,
       'secondary_task': True},
      {'trial': 27,  # 21
       'rand_id': 18,
       'feedback': FEEDBACK_ON,
       'secondary_task': True},
      {'trial': 28,  # 16
       'rand_id': 18,
       'feedback': FEEDBACK_ON,
       'secondary_task': False}
      ]
<commit_msg>update trials for new experiment<commit_after>from __future__ import division, print_function
import numpy as np
from numpy import pi
import pandas as pd

FEEDBACK_ON    =  1
FEEDBACK_OFF   =  0
FEEDBACK_FALSE = -1

def f(x, t):
    return pd.Series((x[0] * 2*pi*x[1] / 240.) * np.cos(2*pi*x[1] * t / 240. + x[3]))

def rescale(x):
    ''' Rescale array to [-1, 1] '''
    x -= min(x)
    x /= 0.5 * max(abs(x))
    return x - 1

def generate_path(trial_number, length=240, fps=60):
    # Sweet, Barbara Townsend, and Leonard J. Trejo.
    # "The identification and modeling of visual cue
    # usage in manual control task experiments." (1999).
    data = [[.50,   6,  0.16],
            [.50,  10,  0.26],
            [.50,  15,  0.39],
            [.50,  23,  0.60],
            [.50,  37,  0.97],
            [.50,  59,  1.54],
            [.05, 101,  2.64],
            [.05, 127,  3.32],
            [.05, 149,  3.90],
            [.05, 179,  4.69],
            [.05, 311,  8.14],
            [.05, 521, 13.64]]

    np.random.seed(trial_number)
    d = pd.DataFrame(data)

    offsets = np.random.uniform(-pi, pi, (100, 12))
    d[3] = offsets[trial_number]

    t = np.linspace(0, length, length * fps)
    sines = d[:-4].apply(f, args=(t,), axis=1).T
    sines.index = t
    sines = sines.sum(axis=1)
    sines = rescale(sines).tolist()

    return sines

ks = [{'trial': 1},
      {'trial': 2},
      {'trial': 3},
      {'trial': 4},
      {'trial': 5},
      {'trial': 6},
      {'trial': 7},
      {'trial': 8},
      {'trial': 9},
      {'trial': 10},
      {'wait': 5*60},
      {'trial': 11},
      {'trial': 12},
      {'trial': 13},
      {'trial': 14},
      {'trial': 15},
      {'trial': 16},
      {'trial': 17},
      {'trial': 18},
      {'trial': 19},
      {'trial': 20}]
<|endoftext|>


<commit_before>from django.shortcuts import render, redirect
from django.urls import reverse
from django.views.generic import ListView
from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView
from django.utils.translation import ugettext_lazy as _
from django.db.models import Sum
from django.contrib import messages
from django.forms import formset_factory
from django.contrib.auth.decorators import login_required

from diventi.core.views import DiventiActionMixin

from .models import Survey, Answer, Question, QuestionGroup
from .forms import AnswerForm, FeaturedSurveyInitForm

from cuser.middleware import CuserMiddleware

class SurveyListView(ListView):

    model = Survey
    context_object_name = 'surveys'
    paginate_by = 6

    def get_queryset(self):
        return Survey.objects.history()

class AnswerListView(ListView):

    model = Answer
    context_object_name = 'answers'

    def get_queryset(self):
        qs = super(AnswerListView, self).get_queryset()
        user = CuserMiddleware.get_user()
        slug = self.kwargs.get('slug', None)
        survey = Survey.objects.published().get(slug=slug)
        qs = qs.filter(survey=survey)
        if user.is_authenticated:
            qs = qs.filter(author=user)
        else:
            author_name = self.kwargs.get('author_name', None)
            qs = qs.filter(author_name=author_name)
        qs = qs.select_related('question__group')
        return qs

    def get_context_data(self, **kwargs):
        context = super(AnswerListView, self).get_context_data(**kwargs)
        slug = self.kwargs.get('slug', None)
        author_name = self.kwargs.get('author_name', None)
        survey = Survey.objects.published().get(slug=slug)
        outcome = survey.outcome
        if outcome is not None:
            answers_outcome = self.get_queryset().aggregate(Sum('choice__score'))
            # Generic calculation of the survey outcome
            # @myself: consider to improve the algorithm
            answers_score_value = answers_outcome['choice__score__sum']
            if answers_score_value:
                if answers_score_value >= outcome.upper_score - outcome.upper_score / 10:
                    answers_outcome['result'] = outcome.upper_outcome
                elif answers_score_value >= outcome.lower_score:
                    answers_outcome['result'] = outcome.medium_outcome
                elif answers_score_value < outcome.lower_score:
                    answers_outcome['result'] = outcome.lower_outcome
                answers_outcome['lower_score'] = outcome.lower_score
                answers_outcome['upper_score'] = outcome.upper_score
                answers_outcome['progress'] = answers_score_value / outcome.upper_score * 100
        else:
            answers_outcome = None
        context['survey'] = survey
        context['answers_outcome'] = answers_outcome
        context['author_name'] = author_name
        return context

class AnswerListViewByName(AnswerListView):

    def get_queryset(self):
        qs = super(AnswerListView, self).get_queryset()
        slug = self.kwargs.get('slug', None)
        author_name = self.kwargs.get('author_name', None)
        survey = Survey.objects.published().get(slug=slug)
        qs = qs.filter(survey=survey)
        qs = qs.filter(author_name=author_name)
        return qs

def survey_questions(request, slug, author_name=None):

    survey = Survey.objects.published().get(slug=slug)

    if survey.public and author_name is None:
        return redirect(reverse('feedbacks:answers-author', args=[slug, author_name]))

    if request.user.is_authenticated:
        author_name = request.user.get_short_name()
        user_has_answered = Answer.objects.filter(author=request.user, survey=survey).exists()
    elif survey.public:
        user_has_answered = Answer.objects.filter(author_name=author_name, survey=survey).exists()
    else:
        request.session['show_login_form'] = 1
        messages.warning(request, _('You should sign in to access that survey.'))
        return redirect(reverse('landing:home'))

    if user_has_answered:
        return redirect(reverse('feedbacks:answers-author', args=[slug, author_name]))

    question_groups = survey.question_groups.all()
    questions = Question.objects.filter(group__in=(question_groups)).order_by('group__order_index')

    DiventiAnswerFormSet = formset_factory(AnswerForm, max_num=1)

    question_data = [{
            'group_title': question.group.title,
            'group_description': question.group.description,
            'question': question,
            'survey': survey,
            'author_name': author_name,
        } for question in questions]

    formset = DiventiAnswerFormSet(request.POST or None, initial=question_data)

    if request.method == 'POST':
        if formset.is_valid():
            for form in formset:
                form.save()
            messages.success(request, _('Your survey has been saved!'))
            return redirect(reverse('feedbacks:answers-author', args=[slug, author_name]))
        else:
            messages.warning(request, _('Please, double check your answers below.'))

    template_name = 'feedbacks/answer_form.html'
    context = {
        'survey': survey,
        'formset': formset,
        'author_name': author_name,
    }
    return render(request, template_name, context)

def new_answers_gate(request, slug):

    try:
        survey = Survey.objects.published().get(slug=slug)
    except Survey.DoesNotExist:
        messages.warning(request, _('The selected survey doesn\'t exist or is not published yet.'))
        return redirect(reverse('landing:home'))

    if survey.public:
        if request.method == 'POST':
            survey_form = FeaturedSurveyInitForm(request.POST)
            if survey_form.is_valid():
                author_name = survey_form.cleaned_data['author_name']
            else:
                messages.warning(request, _('There was an error while processing your survey.'))
                return redirect(reverse('feedbacks:new_answers_gate', args=[survey.slug,]))
        else:
            template_name = 'feedbacks/author_form.html'
            featured_survey_data = {
                'survey': survey,
            }
            survey_form = FeaturedSurveyInitForm(initial = featured_survey_data)
            context = {
                'survey': survey,
                'survey_form': survey_form,
            }
            return render(request, template_name, context)
    else:
        pass

    user_has_answered = False
    user_can_submit = False

    if request.user.is_authenticated:
        user_has_answered = Answer.objects.filter(author=request.user, survey=survey).exists()
        if user_has_answered:
            return redirect(reverse('feedbacks:answers-author', args=[slug, author_name]))
    else:
        user_has_answered = Answer.objects.filter(author_name=author_name, survey=survey).exists()
        if user_has_answered:
            messages.warning(request, _('An other user already picked that name and answered, please choose an other name.'))
            return redirect(reverse('feedbacks:new_answers_gate', args=[survey.slug,]))

    return redirect(reverse('feedbacks:answers-author', args=[slug, author_name]))

<commit_msg>Added email notification when a user completes a survey.<commit_after>from django.shortcuts import render, redirect
from django.urls import reverse
from django.views.generic import ListView
from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView
from django.utils.translation import ugettext_lazy as _
from django.db.models import Sum
from django.contrib import messages
from django.forms import formset_factory
from django.contrib.auth.decorators import login_required
from django.core.mail import send_mail

from diventi.core.views import DiventiActionMixin

from .models import Survey, Answer, Question, QuestionGroup
from .forms import AnswerForm, FeaturedSurveyInitForm

from cuser.middleware import CuserMiddleware

class SurveyListView(ListView):

    model = Survey
    context_object_name = 'surveys'
    paginate_by = 6

    def get_queryset(self):
        return Survey.objects.history()

class AnswerListView(ListView):

    model = Answer
    context_object_name = 'answers'

    def get_queryset(self):
        qs = super(AnswerListView, self).get_queryset()
        user = CuserMiddleware.get_user()
        slug = self.kwargs.get('slug', None)
        survey = Survey.objects.published().get(slug=slug)
        qs = qs.filter(survey=survey)
        if user.is_authenticated:
            qs = qs.filter(author=user)
        else:
            author_name = self.kwargs.get('author_name', None)
            qs = qs.filter(author_name=author_name)
        qs = qs.select_related('question__group')
        return qs

    def get_context_data(self, **kwargs):
        context = super(AnswerListView, self).get_context_data(**kwargs)
        slug = self.kwargs.get('slug', None)
        author_name = self.kwargs.get('author_name', None)
        survey = Survey.objects.published().get(slug=slug)
        outcome = survey.outcome
        if outcome is not None:
            answers_outcome = self.get_queryset().aggregate(Sum('choice__score'))
            # Generic calculation of the survey outcome
            # @myself: consider to improve the algorithm
            answers_score_value = answers_outcome['choice__score__sum']
            if answers_score_value:
                if answers_score_value >= outcome.upper_score - outcome.upper_score / 10:
                    answers_outcome['result'] = outcome.upper_outcome
                elif answers_score_value >= outcome.lower_score:
                    answers_outcome['result'] = outcome.medium_outcome
                elif answers_score_value < outcome.lower_score:
                    answers_outcome['result'] = outcome.lower_outcome
                answers_outcome['lower_score'] = outcome.lower_score
                answers_outcome['upper_score'] = outcome.upper_score
                answers_outcome['progress'] = answers_score_value / outcome.upper_score * 100
        else:
            answers_outcome = None
        context['survey'] = survey
        context['answers_outcome'] = answers_outcome
        context['author_name'] = author_name
        return context

class AnswerListViewByName(AnswerListView):

    def get_queryset(self):
        qs = super(AnswerListView, self).get_queryset()
        slug = self.kwargs.get('slug', None)
        author_name = self.kwargs.get('author_name', None)
        survey = Survey.objects.published().get(slug=slug)
        qs = qs.filter(survey=survey)
        qs = qs.filter(author_name=author_name)
        return qs

def survey_questions(request, slug, author_name=None):

    survey = Survey.objects.published().get(slug=slug)

    if survey.public and author_name is None:
        return redirect(reverse('feedbacks:answers-author', args=[slug, author_name]))

    if request.user.is_authenticated:
        author_name = request.user.get_short_name()
        user_has_answered = Answer.objects.filter(author=request.user, survey=survey).exists()
    elif survey.public:
        user_has_answered = Answer.objects.filter(author_name=author_name, survey=survey).exists()
    else:
        request.session['show_login_form'] = 1
        messages.warning(request, _('You should sign in to access that survey.'))
        return redirect(reverse('landing:home'))

    if user_has_answered:
        return redirect(reverse('feedbacks:answers-author', args=[slug, author_name]))

    question_groups = survey.question_groups.all()
    questions = Question.objects.filter(group__in=(question_groups)).order_by('group__order_index')

    DiventiAnswerFormSet = formset_factory(AnswerForm, max_num=1)

    question_data = [{
            'group_title': question.group.title,
            'group_description': question.group.description,
            'question': question,
            'survey': survey,
            'author_name': author_name,
        } for question in questions]

    formset = DiventiAnswerFormSet(request.POST or None, initial=question_data)

    if request.method == 'POST':
        if formset.is_valid():
            for form in formset:
                form.save()
            send_mail(
                _('Diventi: %(user)s has completed %(survey)s') % {'user': author_name, 'survey': survey.title,},
                _('Dear Diventi authors, new answers are now available for survey %(survey)s.') % {'survey': survey.title,},
                'info@playdiventi.it',
                ['info@playdiventi.it',],
                fail_silently=False,
            )
            messages.success(request, _('Your survey has been saved!'))
            return redirect(reverse('feedbacks:answers-author', args=[slug, author_name]))
        else:
            messages.warning(request, _('Please, double check your answers below.'))

    template_name = 'feedbacks/answer_form.html'
    context = {
        'survey': survey,
        'formset': formset,
        'author_name': author_name,
    }
    return render(request, template_name, context)

def new_answers_gate(request, slug):

    try:
        survey = Survey.objects.published().get(slug=slug)
    except Survey.DoesNotExist:
        messages.warning(request, _('The selected survey doesn\'t exist or is not published yet.'))
        return redirect(reverse('landing:home'))

    if survey.public:
        if request.method == 'POST':
            survey_form = FeaturedSurveyInitForm(request.POST)
            if survey_form.is_valid():
                author_name = survey_form.cleaned_data['author_name']
            else:
                messages.warning(request, _('There was an error while processing your survey.'))
                return redirect(reverse('feedbacks:new_answers_gate', args=[survey.slug,]))
        else:
            template_name = 'feedbacks/author_form.html'
            featured_survey_data = {
                'survey': survey,
            }
            survey_form = FeaturedSurveyInitForm(initial = featured_survey_data)
            context = {
                'survey': survey,
                'survey_form': survey_form,
            }
            return render(request, template_name, context)
    else:
        pass

    user_has_answered = False
    user_can_submit = False

    if request.user.is_authenticated:
        user_has_answered = Answer.objects.filter(author=request.user, survey=survey).exists()
        if user_has_answered:
            return redirect(reverse('feedbacks:answers-author', args=[slug, author_name]))
    else:
        user_has_answered = Answer.objects.filter(author_name=author_name, survey=survey).exists()
        if user_has_answered:
            messages.warning(request, _('An other user already picked that name and answered, please choose an other name.'))
            return redirect(reverse('feedbacks:new_answers_gate', args=[survey.slug,]))

    return redirect(reverse('feedbacks:answers-author', args=[slug, author_name]))

<|endoftext|>