<commit_before>
"""
Communicate with the Chief Electoral Officer's websites of various states.

>>> import json
>>> d = KA_Client.get_voter_info(21, 'ZBG6433064')
>>> print json.dumps(d, sort_keys=True, indent=2)
{
  "ac_name": "Byatarayanapura", 
  "ac_number": "152", 
  "age": "30", 
  "part_number": "246", 
  "relative_first_name": "Subramanian", 
  "relative_last_name": "Panchapakesan", 
  "serial_number": "1994", 
  "sex": "M", 
  "voter_first_name": "Suriya", 
  "voter_last_name": "Subramanian"
}
>>> d = DL_Client.get_voter_info(0, 'XVX0000075')
>>> print json.dumps(d, sort_keys=True, indent=2)
{
  "ac_number": "1", 
  "age": "37", 
  "part_number": "1", 
  "relative_first_name": "MUKESH", 
  "relative_last_name": "KUMAR", 
  "relative_name": "<NAME>", 
  "serial_number": "8", 
  "sex": "Female", 
  "voter_first_name": "SHISHAN", 
  "voter_last_name": "KAUR", 
  "voter_name": "<NAME>"
}
>>> d = TN_Client.get_voter_info(0, 'AYR0562041')
>>> print json.dumps(d, sort_keys=True, indent=2)
{
  "ac_name": "1 - Gummidipoondi", 
  "part_number": "1", 
  "relative_first_name": "", 
  "relative_last_name": "Shankar", 
  "relative_name": "Shankar", 
  "serial_number": "13", 
  "sex": "Female", 
  "voter_first_name": "", 
  "voter_last_name": "Sandhiya", 
  "voter_name": "Sandhiya"
}
>>> d = UP_Client.get_voter_info('09', 'KYC1817881')
>>> print json.dumps(d, sort_keys=True, indent=2)
{
  "ac_number": "73", 
  "age": "75", 
  "part_number": "1", 
  "relative_first_name": "", 
  "relative_last_name": "Chandrapal", 
  "relative_name": "Chandrapal", 
  "serial_number": "46", 
  "sex": "M", 
  "voter_first_name": "Gokul", 
  "voter_last_name": "Singh", 
  "voter_name": "<NAME>"
}
>>> d = UP_Client.get_voter_info('09', 'UP/75/372/0138500')
>>> print json.dumps(d, sort_keys=True, indent=2)
{
  "ac_number": "73", 
  "age": "62", 
  "part_number": "1", 
  "relative_first_name": "Vasadev", 
  "relative_last_name": "Singh", 
  "relative_name": "<NAME>", 
  "serial_number": "52", 
  "sex": "M", 
  "voter_first_name": "Shyodan", 
  "voter_last_name": "Singh", 
  "voter_name": "<NAME>"
}
"""

from bs4 import BeautifulSoup
import re
import urllib
import urllib2
import requests
import logging
import sys
import textwrap
logger = logging.getLogger(__name__)

class CEOClientException(Exception):
    MESSAGE = ''
    def __str__(self):
        return self.MESSAGE

class ConnectionError(CEOClientException):
    MESSAGE = textwrap.dedent(
    """
    Unable to connect to the Chief Election Commissioner's website.
    Probably the site is down. We will try connecting later and update this
    record. You do not have to do anything about this. Once we update the
    record we will notify you.
    """)

class FailedToParseError(CEOClientException):
    MESSAGE = textwrap.dedent(
    """
    Received a response from the Chief Election Commissioner's website, but
    unable to retrive the correct data. It could be that the Voter ID given
    is incorrect. We will look to see if this is an error on our part, and
    if so, update the record. If entered Voter ID is incorect, delete this
    entry.
    """)

class UnsupportedState(CEOClientException):
    MESSAGE = textwrap.dedent(
    """
    Save Your Vote is yet to implement support for your state. We will get
    to it as soon as possible and update this record. We will notify you
    once we obtain information from the Election Commissioner's website.
    """)

class CEOClient:
    @classmethod
    def record_voter_info(klass, existing_voter):
        state = existing_voter.district.state.shortcode
        client = CEO_CLIENTS.get(state, None)
        if not client:
            raise UnsupportedState()
        district_number = existing_voter.district.number
        epic_string = unicode(existing_voter.epic_number).encode('ascii', 'ignore')
        info = client.get_voter_info(district_number, epic_string)
        for (key, value) in info.iteritems():
            setattr(existing_voter, key, value)
        existing_voter.save()
        logger.info('Saved voter details for EPIC: %s' % epic_string)
        return existing_voter

    @classmethod
    def get_voter_info(klass, district_number, epic_string):
        html = klass.get_html_data(district_number, epic_string)
        return klass.parse_html_data(html)

    @classmethod
    def request_params(klass, district_number, epic_string):
        raise NotImplementedError("CEOClient.request_params")

    @classmethod
    def get_html_data(klass, district_number, epic_string):
        datadict = klass.request_params(district_number, epic_string)
        try:
            r = requests.post(klass.CEO_URL, datadict, timeout=8)
        except requests.exceptions.ConnectionError as e:
            logger.warning('Error connecting to CEO website: %s' % e)
            raise ConnectionError()
        except requests.exceptions.HTTPError as e:
            logger.warning('HTTPError: %s' % e)
            raise ConnectionError()
        except requests.exceptions.Timeout as e:
            logger.warning('Timeout error: %s' % e)
            raise ConnectionError()
        except requests.exceptions.TooManyRedirects as e:
            logger.warning('Too many redirects: %s' % e)
            raise ConnectionError()
        except:
            logger.error("Unexpected error: %s" % sys.exc_info()[0])
            raise ConnectionError()
        return r.content

    @classmethod
    def parse_html_data(klass, html):
        raise NotImplementedError("CEOClient.parse_html_data")

    @classmethod
    def split_name(klass, name):
        """
        >>> CEOClient.split_name('<NAME>')
        ('SHISHAN', 'KAUR')
        """
        if not name:
            return (None, None)
        if not isinstance(name, basestring):
            return (None, None)<commit_msg>Increase timeout when talking to CEO website.<commit_after>
"""
Communicate with the Chief Electoral Officer's websites of various states.

>>> import json
>>> d = KA_Client.get_voter_info(21, 'ZBG6433064')
>>> print json.dumps(d, sort_keys=True, indent=2)
{
  "ac_name": "Byatarayanapura", 
  "ac_number": "152", 
  "age": "30", 
  "part_number": "246", 
  "relative_first_name": "Subramanian", 
  "relative_last_name": "Panchapakesan", 
  "serial_number": "1994", 
  "sex": "M", 
  "voter_first_name": "Suriya", 
  "voter_last_name": "Subramanian"
}
>>> del d
>>> d = DL_Client.get_voter_info(0, 'XVX0000075')
>>> print json.dumps(d, sort_keys=True, indent=2)
{
  "ac_number": "1", 
  "age": "37", 
  "part_number": "1", 
  "relative_first_name": "MUKESH", 
  "relative_last_name": "KUMAR", 
  "relative_name": "<NAME>", 
  "serial_number": "8", 
  "sex": "Female", 
  "voter_first_name": "SHISHAN", 
  "voter_last_name": "KAUR", 
  "voter_name": "<NAME>"
}
>>> del d
>>> d = TN_Client.get_voter_info(0, 'AYR0562041')
>>> print json.dumps(d, sort_keys=True, indent=2)
{
  "ac_name": "1 - Gummidipoondi", 
  "part_number": "1", 
  "relative_first_name": "", 
  "relative_last_name": "Shankar", 
  "relative_name": "Shankar", 
  "serial_number": "13", 
  "sex": "Female", 
  "voter_first_name": "", 
  "voter_last_name": "Sandhiya", 
  "voter_name": "Sandhiya"
}
>>> del d
>>> d = UP_Client.get_voter_info('09', 'KYC1817881')
>>> print json.dumps(d, sort_keys=True, indent=2)
{
  "ac_number": "73", 
  "age": "75", 
  "part_number": "1", 
  "relative_first_name": "", 
  "relative_last_name": "Chandrapal", 
  "relative_name": "Chandrapal", 
  "serial_number": "46", 
  "sex": "M", 
  "voter_first_name": "Gokul", 
  "voter_last_name": "Singh", 
  "voter_name": "<NAME>"
}
>>> del d
>>> d = UP_Client.get_voter_info('09', 'UP/75/372/0138500')
>>> print json.dumps(d, sort_keys=True, indent=2)
{
  "ac_number": "73", 
  "age": "62", 
  "part_number": "1", 
  "relative_first_name": "Vasadev", 
  "relative_last_name": "Singh", 
  "relative_name": "<NAME>", 
  "serial_number": "52", 
  "sex": "M", 
  "voter_first_name": "Shyodan", 
  "voter_last_name": "Singh", 
  "voter_name": "<NAME>"
}
"""

from bs4 import BeautifulSoup
import re
import urllib
import urllib2
import requests
import logging
import sys
import textwrap
logger = logging.getLogger(__name__)

class CEOClientException(Exception):
    MESSAGE = ''
    def __str__(self):
        return self.MESSAGE

class ConnectionError(CEOClientException):
    MESSAGE = textwrap.dedent(
    """
    Unable to connect to the Chief Election Commissioner's website.
    Probably the site is down. We will try connecting later and update this
    record. You do not have to do anything about this. Once we update the
    record we will notify you.
    """)

class FailedToParseError(CEOClientException):
    MESSAGE = textwrap.dedent(
    """
    Received a response from the Chief Election Commissioner's website, but
    unable to retrive the correct data. It could be that the Voter ID given
    is incorrect. We will look to see if this is an error on our part, and
    if so, update the record. If entered Voter ID is incorect, delete this
    entry.
    """)

class UnsupportedState(CEOClientException):
    MESSAGE = textwrap.dedent(
    """
    Save Your Vote is yet to implement support for your state. We will get
    to it as soon as possible and update this record. We will notify you
    once we obtain information from the Election Commissioner's website.
    """)

class CEOClient:
    @classmethod
    def record_voter_info(klass, existing_voter):
        state = existing_voter.district.state.shortcode
        client = CEO_CLIENTS.get(state, None)
        if not client:
            raise UnsupportedState()
        district_number = existing_voter.district.number
        epic_string = unicode(existing_voter.epic_number).encode('ascii', 'ignore')
        info = client.get_voter_info(district_number, epic_string)
        for (key, value) in info.iteritems():
            setattr(existing_voter, key, value)
        existing_voter.save()
        logger.info('Saved voter details for EPIC: %s' % epic_string)
        return existing_voter

    @classmethod
    def get_voter_info(klass, district_number, epic_string):
        html = klass.get_html_data(district_number, epic_string)
        return klass.parse_html_data(html)

    @classmethod
    def request_params(klass, district_number, epic_string):
        raise NotImplementedError("CEOClient.request_params")

    @classmethod
    def get_html_data(klass, district_number, epic_string):
        datadict = klass.request_params(district_number, epic_string)
        try:
            r = requests.post(klass.CEO_URL, datadict, timeout=12)
        except requests.exceptions.ConnectionError as e:
            logger.warning('Error connecting to CEO website: %s' % e)
            raise ConnectionError()
        except requests.exceptions.HTTPError as e:
            logger.warning('HTTPError: %s' % e)
            raise ConnectionError()
        except requests.exceptions.Timeout as e:
            logger.warning('Timeout error: %s' % e)
            raise ConnectionError()
        except requests.exceptions.TooManyRedirects as e:
            logger.warning('Too many redirects: %s' % e)
            raise ConnectionError()
        except:
            logger.error("Unexpected error: %s" % sys.exc_info()[0])
            raise ConnectionError()
        return r.content

    @classmethod
    def parse_html_data(klass, html):
        raise NotImplementedError("CEOClient.parse_html_data")

    @classmethod
    def split_name(klass, name):
        """
        >>> CEOClient.split_name('<NAME>')
        ('SHISHAN', 'KAUR')
        """
        if not name:
            return (None, None)
        if not isinstance(name, basestring):
            return (None, None)
{'fraction_non_alphanumeric': 0.12926099957283213, 'fraction_numerical': 0.015805211448099103, 'mean_word_length': 3.4073795180722892, 'pattern_counts': {'":': 106, '<': 15, '<?xml version=': 0, '>': 99, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '641522', 'n_tokens_mistral': 4094, 'n_tokens_neox': 3834, 'n_words': 959}