def __init__( self, *args, **kwargs ):
        
        # ! ==> call parent's __init__()
        super( IndexInfo, self ).__init__()

        # ! ==> declare instance variables
        
        # basics - what index am I?
        self.m_index = -1
        
        # master map of coder ID to coder index info.
        self.m_coder_id_to_info_map = {}
        
        # coder quick reference lookup tables.
        self.m_coder_id_to_instance_map = {}
        self.m_coder_id_to_priority_map = {}
        self.m_prioritized_coder_list = []
                
        # exception helper
        self.m_exception_helper = ExceptionHelper()
        self.m_exception_helper.set_logger_name( self.LOGGER_NAME )
        self.m_exception_helper.logger_debug_flag = True
        self.m_exception_helper.logger_also_print_flag = False
        
        # debug variables
        self.m_debug_output_json_file_path = ""
    def __init__( self, *args, **kwargs ):
        
        # ! ==> call parent's __init__()
        super( IndexHelper, self ).__init__()

        # ! ==> declare instance variables
        
        # quick reference - coder User ID to instance map.
        self.m_coder_id_to_instance_map = {}        

        # master index info map.
        self.m_index_to_info_map = {}

        # limit users included
        self.m_limit_to_user_id_list = []
        self.m_exclude_user_id_list = []
        
        # exception helper
        self.m_exception_helper = ExceptionHelper()
        self.m_exception_helper.set_logger_name( self.LOGGER_NAME )
        self.m_exception_helper.logger_debug_flag = True
        self.m_exception_helper.logger_also_print_flag = False
        
        # debug variables
        self.debug_output_json_file_path = ""
    def __init__( self,
                  coder_user_id_IN = None,
                  coder_user_instance_IN = None,
                  index_IN = None,
                  priority_IN = None,
                  *args,
                  **kwargs ):
        
        # ! ==> call parent's __init__()
        super( CoderIndexInfo, self ).__init__()

        # ! ==> declare instance variables
        
        # coder info. init.
        self.m_coder_user_id = None
        self.m_coder_user_instance = None
        self.m_index = None
        self.m_priority = None        
        
        # set coder info.
        self.set_coder_user_id( coder_user_id_IN )
        self.set_coder_user_instance( coder_user_instance_IN )
        self.set_index( index_IN )
        self.set_priority( priority_IN )

        # exception helper
        self.m_exception_helper = ExceptionHelper()
        self.m_exception_helper.set_logger_name( self.LOGGER_NAME )
        self.m_exception_helper.logger_debug_flag = True
        self.m_exception_helper.logger_also_print_flag = False
Ejemplo n.º 4
0
class AbstractTimeSeriesDataModel( models.Model ):

    #============================================================================
    # constants-ish
    #============================================================================

    DEBUG_FLAG = False

    # time period types
    TIME_PERIOD_HOURLY = "hourly"

    #============================================================================
    # Django model fields
    #============================================================================
    
    start_date = models.DateTimeField( null = True, blank = True )
    end_date = models.DateTimeField( null = True, blank = True )
    time_period = models.ForeignKey( Time_Period, null = True, blank = True )
    time_period_type = models.CharField( max_length = 255, null = True, blank = True ) # - hourly, by minute, etc.
    time_period_index = models.IntegerField( null = True, blank = True )
    time_period_category = models.CharField( max_length = 255, null = True, blank = True )
    time_period_label = models.CharField( max_length = 255, null = True, blank = True ) # could give each hour, etc. a separate identifier "start+1", "start+2", etc. - not naming _id to start, so you leave room for this to be a separate table.
    aggregate_index = models.IntegerField( null = True, blank = True ) # a separate index you can use to keep track of overall order within a time-series that you separate out into multiple types - "before" and "after", for example - time_period_index can be counter within before or after, aggregate index can be unique through both before and after.
    original_id = models.CharField( max_length = 255, null = True, blank = True )
    original_name = models.CharField( max_length = 255, null = True, blank = True )
    filter_type = models.CharField( max_length = 255, null = True, blank = True ) # - place to keep track of different filter types, if you want.  Example: "text_contains"
    filter_value = models.CharField( max_length = 255, null = True, blank = True )
    match_value = models.CharField( max_length = 255, null = True, blank = True )
    match_count = models.IntegerField( null = True, blank = True, default = 0 )
    filter_1 = models.BooleanField( default = False )
    match_count_1 = models.IntegerField( null = True, blank = True, default = 0 )
    filter_2 = models.BooleanField( default = False )
    match_count_2 = models.IntegerField( null = True, blank = True, default = 0 )
    filter_3 = models.BooleanField( default = False )
    match_count_3 = models.IntegerField( null = True, blank = True, default = 0 )
    filter_4 = models.BooleanField( default = False )
    match_count_4 = models.IntegerField( null = True, blank = True, default = 0 )
    filter_5 = models.BooleanField( default = False )
    match_count_5 = models.IntegerField( null = True, blank = True, default = 0 )
    filter_6 = models.BooleanField( default = False )
    match_count_6 = models.IntegerField( null = True, blank = True, default = 0 )
    filter_7 = models.BooleanField( default = False )
    match_count_7 = models.IntegerField( null = True, blank = True, default = 0 )
    filter_8 = models.BooleanField( default = False )
    match_count_8 = models.IntegerField( null = True, blank = True, default = 0 )
    filter_9 = models.BooleanField( default = False )
    match_count_9 = models.IntegerField( null = True, blank = True, default = 0 )
    filter_10 = models.BooleanField( default = False )
    match_count_10 = models.IntegerField( null = True, blank = True, default = 0 )
    create_date = models.DateTimeField( auto_now_add = True )
    last_update = models.DateTimeField( auto_now = True )

    #============================================================================
    # Instance variables
    #============================================================================
    
    
    m_exception_helper = ExceptionHelper()
    
    
    #============================================================================
    # meta class
    #============================================================================

    # meta class so we know this is an abstract class.
    class Meta:
        abstract = True

    # add string output method.
    

    #============================================================================
    # class methods
    #============================================================================


    @classmethod
    def get_instance( cls,
                      original_name_IN = "",
                      original_id_IN = "",
                      start_dt_IN = None,
                      end_dt_IN = None,
                      time_period_type_IN = "",
                      time_period_category_IN = "",
                      time_period_index_IN = -1,
                      update_existing_IN = True,
                      *args,
                      **kwargs ):

        '''
        Accepts a subreddit full ID; start and end datetime; time period
           type, category and index; and flag telling whether we are updating
           existing.  If not updating existing, returns new instance.  If
           updating, uses values to filter time-series records to find existing
           record for a given subreddit in a given set.  If you are working with
           existing time-series records created with make_data, to make it more
           likely you'll get matches, pass the same values to these parameters
           that you did when you created the data (so same time period type, time
           period category, start and end date of the period, and index within
           category).  If it finds none or more than one, returns None.
                    
        Parameters:
        - original_name_IN - name of row we are trying to find time-series record(s) for.
        - original_id_IN - original ID of row we are trying to find time-series record(s) for.
        - start_dt_IN - datetime.datetime instance of date and time on which you want to start deriving time-series data.
        - end_dt_IN - datetime.datetime instance of date and time on which you want to stop deriving time-series data.
        - time_period_type_IN - (optional) time period type value you want stored in each time-series record.  Defaults to empty string.
        - time_period_category_IN - (optional) category used in labeling.  If set, this was stored in time_period_category, appended to the front of an integer counter that counts up each time period, which was then stored in time_period_label.
        '''
        
        # return reference
        instance_OUT = None
        
        # declare variables
        me = "get_instance"
        update_existing = False
        row_match_rs = None
        debug_flag = cls.DEBUG_FLAG
        
        # updating existing?
        update_existing = update_existing_IN

        # Are we updating?
        if ( ( update_existing ) and ( update_existing != None ) and ( update_existing == True ) ):

            if ( debug_flag == True ):
                print( "In " + me + " - updating, looking up existing record" )
            #-- END DEBUG --#

            # first, see if we can find a match for this row.  Can filter on
            #    start date, end date, original name, original ID, category,
            #    type, and/or index.
            row_match_rs = cls.lookup_records( original_name_IN = original_name_IN,
                                               original_id_IN = original_id_IN,
                                               start_dt_IN = start_dt_IN,
                                               end_dt_IN = end_dt_IN,
                                               time_period_type_IN = time_period_type_IN,
                                               time_period_category_IN = time_period_category_IN,
                                               time_period_index_IN = time_period_index_IN )
            
            # check to see if we got anything back.
            if ( row_match_rs.count() == 1 ):
            
                # found one - update it.
                instance_OUT = row_match_rs[ 0 ]

                if ( debug_flag == True ):
                    print( "==> Found 1 ( id = " + str( instance_OUT.pk ) + " )." )
                #-- END DEBUG --#
            
            elif ( row_match_rs.count() > 1 ):
            
                # error - what to do?
                instance_OUT = None

                if ( debug_flag == True ):
                    print( "==> More than one match found ( " + str( row_match_rs.count() ) + " )" )
                #-- END DEBUG --#
            
            else:
            
                # no existing row - create new instance of this class.
                instance_OUT = cls()

                if ( debug_flag == True ):
                    print( "==> Found 0." )
                #-- END DEBUG --#
                
            #-- END check to see if we have an existing row to update. --#
            
        else:
        
            # not updating - create new row.
            instance_OUT = cls()

            if ( debug_flag == True ):
                print( "In " + me + " - Not updating." )
            #-- END DEBUG --#
        
        #-- END check to see if updating existing --#
        
        return instance_OUT
        
    #-- END method get_instance() --#


    @classmethod
    def lookup_records( cls,
                        original_name_IN = "",
                        original_id_IN = "",
                        start_dt_IN = None,
                        end_dt_IN = None,
                        time_period_type_IN = "",
                        time_period_category_IN = "",
                        time_period_index_IN = -1,
                        *args,
                        **kwargs ):

        '''
        Accepts an original name, origianl ID, start and end datetime, time
           period type, and time period label.  Uses these values to filter
           time-series records.  If you are working with existing time-series
           records created with make_data, to make it more likely you'll get
           correct matches, pass the same values to these parameters that you did
           when you created the data (so same time period type, time period
           category, time period index, start and end date of the period, and
           original ID).
                    
        Parameters:
        - original_name_IN - name of subreddit we are trying to find time-series record(s) for.
        - original_id_IN - name of subreddit we are trying to find time-series record(s) for.
        - start_dt_IN - datetime.datetime instance of date and time on which you want to start deriving time-series data.
        - end_dt_IN - datetime.datetime instance of date and time on which you want to stop deriving time-series data.
        - time_period_type_IN - (optional) time period type value you want stored in each time-series record.  Defaults to empty string.
        - time_period_category_IN - (optional) label to use in labeling.  If set, this is appended to the front of an integer counter that counts up each time period, is stored in time_period_label.  If not set, the integer time period counter is the only thing stored in time period label.
        - time_period_index_IN - (optional) index to keep track of time periods within a category.  Generated values are non-zero, so only included in filter if value is greater than 0.
        '''
        
        # return reference
        rs_OUT = None
        
        # declare variables.
        
        # start out lookup by getting all objects.
        rs_OUT = cls.objects.all()
        
        # for each parameter, check for a non-empty value, if present, filter.
        
        # original name
        if ( ( original_name_IN ) and ( original_name_IN != "" ) ):
        
            rs_OUT = rs_OUT.filter( original_name__iexact = original_name_IN )
        
        #-- END check for subreddit name --#
        
        # original ID
        if ( ( original_id_IN ) and ( original_id_IN != "" ) ):
        
            rs_OUT = rs_OUT.filter( original_id__iexact = original_id_IN )
        
        #-- END check for subreddit reddit ID name --#
        
        # start date
        if ( ( start_dt_IN ) and ( start_dt_IN != None ) ):
        
            rs_OUT = rs_OUT.filter( start_date = start_dt_IN )
        
        #-- END check for start date --#
        
        # end date
        if ( ( end_dt_IN ) and ( end_dt_IN != "" ) ):
        
            rs_OUT = rs_OUT.filter( end_date = end_dt_IN )
        
        #-- END check for end date --#
        
        # time period type
        if ( ( time_period_type_IN ) and ( time_period_type_IN != "" ) ):
        
            rs_OUT = rs_OUT.filter( time_period_type__iexact = time_period_type_IN )
        
        #-- END check for time period type value --#
        
        # time period category
        if ( ( time_period_category_IN ) and ( time_period_category_IN != "" ) ):
        
            rs_OUT = rs_OUT.filter( time_period_category__iexact = time_period_category_IN )
        
        #-- END check for time period category value --#
        
        # time period index
        if ( ( time_period_index_IN ) and ( time_period_index_IN > 0 ) ):
        
            rs_OUT = rs_OUT.filter( time_period_index = time_period_index_IN )
        
        #-- END check for time period index value --#
        
        return rs_OUT

    #-- END class method lookup_records --#
    

    @classmethod
    def process_exception( cls, exception_IN = None, message_IN = "", print_details_IN = True, *args, **kwargs ):
    
        # return reference
        status_OUT = ""
    
        # declare variables
        exception_helper = None
        exception_message = ""
        exception_status = ""

        # Get exception helper class.
        exception_helper = cls.m_exception_handler
                                
        # process the exception
        exception_message = message_IN
        exception_status = exception_helper.process_exception( exception_IN = exception_IN, message_IN = exception_message, print_details_IN = print_details_IN )
        
        # set status to description of exception
        status_OUT = exception_helper.last_exception_details
        
        return status_OUT
        
    #-- END method process_exception() --#
        
        
    #============================================================================
    # instance methods
    #============================================================================


    def __str__(self):
        
        # return reference
        string_OUT = ""
        
        # id?
        if ( ( self.id ) and ( self.id != None ) and ( self.id > 0 ) ):
        
            string_OUT += str( self.id )
        
        #-- END check to see if id --#
        
        # start date?
        if( self.start_date ):
        
            string_OUT += " - " + str( self.start_date )
        
        #-- END check to see if start_date --#

        # end date?
        if( self.end_date ):
        
            string_OUT += " --> " + str( self.end_date )
        
        #-- END check to see if end_date --#
        
        # label.
        if ( self.time_period_label ):
        
            string_OUT += " - " + self.time_period_label
        
        #-- END check to see if time_period_label --#

        # original ID.
        if ( self.original_id ):
        
            string_OUT += " - ID: " + self.original_id
        
        #-- END check to see if original_id --#

        # original name.
        if ( self.original_name ):
        
            string_OUT += " - Name: " + self.original_name
        
        #-- END check to see if original_name --#

        return string_OUT
Ejemplo n.º 5
0
    def test_URL(self, *args, **kwargs):

        """
        Retrieves the full URL from the current instance.  Uses the urllib2
           library to load the page.  If the page loads, sets the is_url_ok flag
           to true.  Uses the results of the page load to see if there was a
           redirect (status of 301 or 302).  If so, parses and stores information
           on the final URL that was loaded in the redirect_* fields in this
           object.  Returns a status message that starts with the OK flag, then
           describes whether a redirect occurred, and if so, where the redirect
           went.
           
        Preconditions:
        - must have loaded data into this instance from a row in the database.
        """

        # return reference
        status_OUT = ""

        # declare variables
        me = "test_URL"
        http_helper = None
        url_to_test = ""
        redirect_url = ""
        redirect_status_list = ""
        redirect_status_count = -1
        last_redirect_status = -1
        exception_helper = None
        exception_message = ""
        exception_status = ""

        # create HTTP Helper
        http_helper = Http_Helper()

        # get URL.
        url_to_test = self.full_url

        # got a full URL?
        if (not url_to_test) or (url_to_test == None) or (url_to_test == ""):

            # no.  Make one out of domain.
            url_to_test = "http://" + self.domain_name

            # got a path?
            if (self.domain_path) and (self.domain_path != None) and (self.domain_path != ""):

                # yes.  Append it, as well.
                url_to_test += self.domain_path

            # -- END check to see if domain path. --#

        # -- END check to see if URL --#

        # now, see if we have a URL again.
        if (url_to_test) and (url_to_test != None) and (url_to_test != ""):

            # try/except to capture problems with URL not resolving at all.
            try:

                # we have a URL.  Use the HTTP helper to test.
                redirect_url = http_helper.get_redirect_url_mechanize(url_to_test)

                # see if there is a status code.
                redirect_status_list = http_helper.redirect_status_list
                redirect_status_count = len(redirect_status_list)
                if redirect_status_count > 0:

                    # yes.  Get latest one (use pop()).
                    last_redirect_status = redirect_status_list.pop()

                # -- END check to see if any statuses --#

                # got anything back?
                if (redirect_url) and (redirect_url != None) and (redirect_url != ""):

                    # yes.  Update the record.

                    # URL is OK.
                    self.is_url_ok = True

                    # store HTTP status code.
                    self.redirect_status = last_redirect_status

                    # store full URL.
                    self.redirect_full_url = redirect_url

                    # parse and store components of URL.
                    self.parse_and_store_URL(URL_IN=redirect_url, is_redirect_IN=True)

                    # set status to Success!
                    status_OUT = self.STATUS_SUCCESS

                else:

                    # no.  No exception, not redirect.  Just a normal URL.
                    self.is_url_ok = True

                    # set status
                    status_OUT = "Attempt to find redirect returned no URL.  Test URL = " + url_to_test

                    # got a status?
                    if (last_redirect_status) and (last_redirect_status > 0):

                        # there is one.  Append it to message.
                        status_OUT += "; HTTP status code: " + str(last_redirect_status)

                    # -- END check to see if HTTP status code --#

                # -- END check to see if redirect URL --#

            except Exception as e:

                # likely URL not found.  URL is not OK, do nothing else.
                self.is_url_ok = False

                # URLError (and child HTTPError) will have a "reason".
                if hasattr(e, "reason"):

                    # yes.  Store it in the redirect_message field.
                    self.redirect_message = e.reason

                # -- END check to see if "reason" --#

                # HTTPError will have an HTTP status "code".
                if hasattr(e, "code"):

                    # yes, store it in the redirect_status field.
                    self.redirect_status = e.code

                # -- END check to see if there is a code.

                # make exception helper class.
                exception_helper = ExceptionHelper()

                # process the exception
                exception_message = "ERROR - Exception caught in " + me + " trying to resolve URL " + url_to_test + "."
                exception_status = exception_helper.process_exception(
                    exception_IN=e, message_IN=exception_message, print_details_IN=False
                )

                # set status to description of exception
                status_OUT = exception_helper.last_exception_details

            # -- END try/except to deal with unknown domains/URLs. --#

            # save the results.
            self.save()

        else:

            status_OUT = "ERROR - Could not find a URL to test for this record."

        # -- END check to make sure we have a URL --#

        return status_OUT
class IndexHelper( object ):
    
    
    #----------------------------------------------------------------------
    # constants-ish
    #----------------------------------------------------------------------    


    # Logger name
    LOGGER_NAME = "context_analysis.reliability.coder_index_builder"

    # information about table.
    TABLE_MAX_CODERS = 10
    
    # INDEX_INFO_* variables
    INDEX_INFO_INDEX = "index"
    INDEX_INFO_PRIORITIZED_CODER_LIST = "prioritized_coder_list"
    INDEX_INFO_CODER_ID_TO_PRIORITY_MAP = "coder_id_to_priority_map"
    
    # index-to-coder mappings
    MAPPING_INDEX_TO_CODER = "index-to-coder"
    MAPPING_CODER_TO_INDEX = "coder-to-index"
    
    # DEBUG
    DEBUG = False

    
    #----------------------------------------------------------------------------
    # instance methods
    #----------------------------------------------------------------------------


    def __init__( self, *args, **kwargs ):
        
        # ! ==> call parent's __init__()
        super( IndexHelper, self ).__init__()

        # ! ==> declare instance variables
        
        # quick reference - coder User ID to instance map.
        self.m_coder_id_to_instance_map = {}        

        # master index info map.
        self.m_index_to_info_map = {}

        # limit users included
        self.m_limit_to_user_id_list = []
        self.m_exclude_user_id_list = []
        
        # exception helper
        self.m_exception_helper = ExceptionHelper()
        self.m_exception_helper.set_logger_name( self.LOGGER_NAME )
        self.m_exception_helper.logger_debug_flag = True
        self.m_exception_helper.logger_also_print_flag = False
        
        # debug variables
        self.debug_output_json_file_path = ""
        
    #-- END method __init__() --#
    

    def __str__( self ):
        
        # return reference
        string_OUT = ""
        
        # declare variables
        current_value = None
        current_value_label = None
        field_output_list = []
        
        current_value = self.get_index_to_info_map()
        current_value_label = "index-info-map"
        if ( current_value is not None ):
        
            field_output_list.append( str( current_value_label ) + ": " + str( current_value ) )
            
        #-- END check to see if coder_user_id --#

        # DEBUG?
        if ( self.DEBUG == True ):

            current_value = self.get_coder_id_to_instance_map()
            current_value_label = "id-to-instance-info"
            if ( current_value is not None ):
            
                field_output_list.append( str( current_value_label ) + ": " + str( current_value ) )
                
            #-- END check to see if coder_user_id --#
    
            current_value = self.get_limit_to_user_id_list()
            current_value_label = "limit-users"
            if ( current_value is not None ):
            
                field_output_list.append( str( current_value_label ) + ": " + str( current_value ) )
                
            #-- END check to see if coder_user_id --#
    
        #-- END check to see if debug --#

        # convert output list to string
        string_OUT = "\n====>".join( field_output_list )
                
        return string_OUT
        
    #-- END method __str__() --#


    def add_coder_at_index( self, coder_id_IN, index_IN, priority_IN = None, *args, **kwargs ):
        
        '''
        Accepts a coder ID and an index.  Updates all the stuff in this instance
            to make sure the coder is tied to the index passed in.
        '''
        
        # return reference
        status_OUT = ""
        
        # declare variables
        me = "add_coder_at_index"
        my_logger = None
        logging_message = None
        coder_id_to_instance_dict = {}
        limit_to_user_id_list = []
        coder_user_id = -1
        coder_index = -1
        coder_priority = None
        is_valid_index = False
        index_info = None
        coder_user = None
        add_status = None
        debug_flag = self.DEBUG
        
        # init logger
        my_logger = self.m_exception_helper
        
        # get maps from instance
        coder_id_to_instance_dict = self.get_coder_id_to_instance_map()
        limit_to_user_id_list = self.get_limit_to_user_id_list()
        
        # init from input parameters.
        coder_user_id = coder_id_IN
        coder_index = index_IN
        coder_priority = priority_IN
        
        # ==> DEBUG
        if ( debug_flag == True ):
            # top of add.
            logging_message = "user ID = {user_id}; index = {index}; priority = {priority}".format( user_id = str( coder_user_id ), index = str( coder_index ), priority = str( coder_priority ) )
            my_logger.output_debug_message( logging_message, method_IN = me, indent_with_IN = "====> ", do_print_IN = True )
        #-- END DEBUG --#
                    
        # got ID?
        if ( ( coder_user_id is not None ) and ( int( coder_user_id ) > 0 ) ):

            # do we have a valid index value?
            is_valid_index = self.is_index_valid( coder_index )
            if ( is_valid_index == True ):
            
                # yes.  Retrieve index info.
                index_info = self.get_info_for_index( coder_index )
                
                # ==> DEBUG
                if ( debug_flag == True ):
                    # top of add.
                    logging_message = "index_info BEFORE: {index_info_instance}".format( index_info_instance = str( index_info ) )
                    my_logger.output_debug_message( logging_message, method_IN = me, indent_with_IN = "====> ", do_print_IN = True )
                #-- END DEBUG --#
            
                # add the coder.
                add_status = index_info.add_coder( coder_user_id, coder_priority )
                
                # ==> DEBUG
                if ( debug_flag == True ):
                    # top of add.
                    logging_message = "index_info AFTER ({status}): {index_info_instance}".format( status = add_status, index_info_instance = str( index_info ) )
                    my_logger.output_debug_message( logging_message, method_IN = me, indent_with_IN = "====> ", do_print_IN = True )
                #-- END DEBUG --#
            
                # Lookup the user.
                coder_user = User.objects.get( id = coder_user_id )
                
                # add to internal map of coder ID to User instance.
                coder_id_to_instance_dict[ coder_user_id ] = coder_user

                # is user in approved coder user list?
                if ( coder_user_id not in limit_to_user_id_list ):
                
                    # not in list - add.
                    limit_to_user_id_list.append( coder_user_id )
                    
                #-- END check to see if user in approved coder user list. --#
                
                # ==> DEBUG
                if ( debug_flag == True ):
                    # top of add.
                    logging_message = "index_to_info_map: {index_to_info}".format( index_to_info = str( self.get_index_to_info_map() ) )
                    my_logger.output_debug_message( logging_message, method_IN = me, indent_with_IN = "====> ", do_print_IN = True )
                #-- END DEBUG --#
            
            else:
            
                # no index - broken.
                status_OUT = "No index - can't associate user with no index."
            
            #-- END check to see if index present. --#
        
        else:
        
            # no coder ID - broken.
            status_OUT = "No coder ID - can't associate user if no user."
        
        #-- END check to see if valid ID. --#

        return status_OUT        
        
    #-- END method add_coder_at_index() --#
    

    def build_index_info( self, *args, **kwargs ):
        
        '''
        Loops over indices represented in index_to_info_map.  For each, in
            IndexInfo instance, calls build_index_info().
            
        Updated information is stored within each IndexInfo instance, and in the
            IndexHelper instance.  Returns index-to-info dictionary that is
            housed inside this helper.
        '''
        
        # return reference
        index_to_info_map_OUT = {}
        
        # declare variables
        index_to_info_dict = None
        current_index = None
        current_index_info = None
        
        # get index-to-info map
        index_to_info_dict = self.get_index_to_info_map()
        
        # loop over indexes present in map.
        for current_index, current_index_info in six.iteritems( index_to_info_dict ):
        
            # call build_index_info() on info object, forcing rebuild.
            current_index_info.build_index_info( *args, **kwargs )
            
        #-- END loop over IndexInfo instances. --#
        
        index_to_info_map_OUT = index_to_info_dict
        
        return index_to_info_map_OUT
        
    #-- END method build_index_info() --#


    def get_coder_for_index( self, index_IN ):
        
        '''
        Accepts a coder index.  Uses it to get ID of coder associated with that
           index with the highest priority, and returns instance of that User.
           If none found, returns None.
        '''
        
        # return reference
        instance_OUT = None
        
        # declare variables
        is_valid_index = False
        index_info = None
        
        # do we have a valid index value?
        is_valid_index = self.is_index_valid( index_IN )
        if ( is_valid_index == True ):

            # get index info for the requested index
            index_info = self.get_info_for_index( index_IN )
            
            # call get_coder_for_index() there.
            instance_OUT = index_info.get_coder_for_index()
            
        else:
        
            # bad index - return None.
            instance_OUT = None
        
        #-- END check to see if valid index. --#
        
        return instance_OUT        
        
    #-- END method get_coder_for_index() --#
    
        
    def get_coder_id_to_instance_map( self ):
        
        '''
        Retrieves nested m_coder_id_to_instance_map from this instance.
        '''
        
        # return reference
        value_OUT = None
        
        # get value and return it.
        value_OUT = self.m_coder_id_to_instance_map
        
        return value_OUT
        
    #-- END method get_coder_id_to_instance_map() --#


    def get_coders_for_index( self, index_IN ):
        
        '''
        Accepts a coder index.  Uses it to get list of User instances of coders
            associated with that index, in priority order, highest first to
            lowest last.  If none found, returns empty list.
            
        Postconditions: If there are two coders with the same priority, they
            will be together in the list in the appropriate position for their
            priority, but arbitrarily ordered from invocation to invocation (so
            in no particular order).  You've been warned.  If you care, don't
            assign two coders the same priority, and/or don't have multiple
            coders assigned to a given index with no priorities.
        '''
        
        # return reference
        coder_list_OUT = []
        
        # declare variables
        me = "get_coders_for_index"
        debug_message = ""
        is_valid_index = False
        index_info = None
        
        # do we have a valid index value?
        is_valid_index = self.is_index_valid( index_IN )
        if ( is_valid_index == True ):

            # get index info
            index_info = self.get_info_for_index( index_IN )
            
            # ask for coders from index_info.
            coder_list_OUT = index_info.get_coders_for_index()
            
        else:
        
            # bad index - return empty list.
            coder_list_OUT = []
        
        #-- END check to see if valid index. --#
                
        return coder_list_OUT        
        
    #-- END method get_coders_for_index() --#
    
        
    def get_exclude_user_id_list( self ):
        
        '''
        Retrieves nested m_exclude_user_id_list from this instance.
        '''
        
        # return reference
        value_OUT = None
        
        # get value and return it.
        value_OUT = self.m_exclude_user_id_list
        
        return value_OUT
        
    #-- END method get_exclude_user_id_list() --#


    def get_info_for_index( self, index_IN ):
        
        '''
        Accepts a coder index.  Retrieves IndexInfo instance for that index.
        '''
        
        # return reference
        instance_OUT = None
        
        # declare variables
        is_valid_index = False
        index_to_info_dict = None
        my_index_info = None

        # do we have a valid index value?
        is_valid_index = self.is_index_valid( index_IN )
        if ( is_valid_index == True ):

            # valid - retrieve m_index_to_info_map
            index_to_info_dict = self.get_index_to_info_map()
    
            # Is this index in the map?
            if ( index_IN not in index_to_info_dict ):
            
                # no.  Create IndexInfo, add it to the map.
                my_index_info = IndexInfo()
                my_index_info.set_index( index_IN )
                index_to_info_dict[ index_IN ] = my_index_info
                
            #-- END check to see if info for index. --#
            
            # get info from index to info map
            instance_OUT = index_to_info_dict.get( index_IN, None )

        else:
        
            # no index, no instance
            instance_OUT = None
            
        #-- END check to see if valid index value --#
        
        return instance_OUT        
        
    #-- END method get_info_for_index() --#
    
        
    def get_index_to_info_map( self, *args, **kwargs ):
        
        '''
        Checks to see if we've already generated index_to_info_map.  If so,
            returns it.  If not, builds it, stores it, then returns it.
        '''
        
        # return reference
        index_to_info_map_OUT = None
        
        # see if we have a map already populated.
        index_to_info_map_OUT = self.m_index_to_info_map
        
        # got anything?
        if ( ( index_to_info_map_OUT is None )
            or ( isinstance( index_to_info_map_OUT, dict ) == False )
            or ( len( index_to_info_map_OUT ) <= 0 ) ):
        
            # no.  Make instance, store it, then return it.
            index_to_info_map_OUT = {}
            self.m_index_to_info_map = index_to_info_map_OUT
            
        #-- END check to see if map already made --#
            
        return index_to_info_map_OUT        
        
    #-- END method get_index_to_info_map() --#
    
        
    def get_limit_to_user_id_list( self ):
        
        '''
        Retrieves nested m_limit_to_user_id_list from this instance.
        '''
        
        # return reference
        value_OUT = None
        
        # get value and return it.
        value_OUT = self.m_limit_to_user_id_list
        
        return value_OUT
        
    #-- END method get_limit_to_user_id_list() --#


    def is_index_valid( self, index_IN ):
        
        '''
        Checks the index passed in to see if it is valid:
        - not None
        - integer
        - greater than 0
        - less then or equal to self.TABLE_MAX_CODERS
        
        If valid, returns True.  If not, returns False.
        '''
        
        # return reference
        is_valid_OUT = False
        
        # declare variables
        coder_index = None
        
        # init coder_index
        coder_index = index_IN
    
        # do we have a valid index value?
        if ( ( coder_index is not None )
            and ( isinstance( coder_index, six.integer_types ) == True )
            and ( coder_index > 0 )
            and ( coder_index <= self.TABLE_MAX_CODERS ) ):
            
            # valid.
            is_valid_OUT = True
            
        else:
        
            # not valid.
            is_valid_OUT = False
            
        #-- END index validity check. --#
        
        return is_valid_OUT
        
    #-- END method is_index_valid() --#


    def map_index_to_coder_for_article( self, article_IN, mapping_type_IN = MAPPING_INDEX_TO_CODER, *args, **kwargs ):

        '''
        Accepts an article for which we want to pick a coder for each index
            in our output.  For a given article, get index info, then for each
            index with coders, go through the prioritized list of coders and use
            the first that has an Article_Data in the current article.  Use this
            information to build a map of indices to coder User instances, and 
            then loop over that in processing (so no longer doing something with
            every coder, just looping over indices that had at least one coder).
            
        Preconditions: This object needs to have been configured with at least
            one coder assigned to an index.
            
        Postconditions: Returns dictionary that maps each index that has been
            assigned one or more coders to the User instance of coder who should
            be used to provide data for that index for the provided article.
        '''

        # return reference
        map_OUT = {}

        # declare variables - coding processing.
        me = "map_index_to_coder_for_article"
        my_logger = None
        article_data_qs = None
        index_to_info_map = None
        index = -1
        index_info = None
        current_coder = None
        current_coder_id = -1
        current_coder_index_list = None
        found_coder_for_index = False
        
        # init logger
        my_logger = self.m_exception_helper        
        
        # article
        if ( article_IN is not None ):
    
            # retrieve the Article_Data QuerySet.    
            article_data_qs = article_IN.article_data_set.all()

            # loop over indexes that have info (so just those that are
            #     configured, not all).  For each, call IndexInfo method
            #     get_coder_for_article() to get coder for article, add it to
            #     the return map.
            map_OUT = {}
            
            # to start, get index-to-info map
            index_to_info_map = self.get_index_to_info_map()

            # loop.
            for current_index, index_info in six.iteritems( index_to_info_map ):
            
                # init
                found_coder_for_index = False
                current_coder = None
            
                # use index_info to get coder for article.
                current_coder = index_info.get_coder_for_article( article_IN )
                
                # got something?
                if ( current_coder is not None ):
                
                    # got one.  How do we map?
                    if ( mapping_type_IN == self.MAPPING_INDEX_TO_CODER ):
                    
                        # Place coder in map of index to coder...
                        map_OUT[ current_index ] = current_coder
                    
                    elif ( mapping_type_IN == self.MAPPING_CODER_TO_INDEX ):
                    
                        # already in map? - coders can map to multiple indices.
                        if ( current_coder not in map_OUT ):
                        
                            # no - add list for coder.
                            map_OUT[ current_coder ] = []
                            
                        #-- END check to see if coder already in map --#
                        
                        # get coder's list.
                        current_coder_index_list = map_OUT.get( current_coder, None )
                        
                        # add index to list if not already there.
                        if ( current_index not in current_coder_index_list ):
                        
                            # not in list - add.
                            current_coder_index_list.append( current_index )
                            
                        #-- END check to see if index in coder's index list. --#
                        
                    #-- END check to see mapping type --#

                    # ...and set found flag to True to avoid
                    #     more lookups.
                    found_coder_for_index = True
                    
                else:
                
                    # no coder found.
                    found_coder_for_index = False
                
                #-- END check to see if coder for article. --#
                    
            #-- END loop over index info --#
            
        #-- END check to see if article actually passed in. --#
        
        return map_OUT
        
    #-- END method map_index_to_coder_for_article() --#


    def set_index_to_info_map( self, map_IN, *args, **kwargs ):
        
        '''
        Accepts index_to_info_map, stores it internally.
        '''
        
        # return reference
        status_OUT = StatusContainer()
        
        # declare variables
        me = "set_index_to_info_map"
        
        # init status
        status_OUT.set_status_code( StatusContainer.STATUS_CODE_SUCCESS )
        
        # store whatever is passed in.
        self.m_index_to_info_map = map_IN
        
        return status_OUT
station_tag_line = ""
station_org_url = ""
station_description = ""
current_domain_name = ""
current_domain_path = ""
current_source = ""
current_source_details = ""
current_domain_type = ""
current_is_news = True

#===============================================================================#
# Code
#===============================================================================#

# initialize exception helper
my_exception_helper = ExceptionHelper()

# set up API key
my_api_key = "<API_key>"

# configure database helper
db_host = "localhost"
db_port = 3306
db_username = "******"
db_password = "******"
db_database = "<database_name>"

# capture start datetime
start_dt = datetime.datetime.now()

# get instance of mysqldb helper
class IndexInfo( object ):
    
    
    #----------------------------------------------------------------------
    # constants-ish
    #----------------------------------------------------------------------    


    # Logger name
    LOGGER_NAME = "context_analysis.reliability.index_info"

    # information about table.
    TABLE_MAX_CODERS = 10
    
    # INDEX_INFO_* variables
    INDEX_INFO_INDEX = "index"
    INDEX_INFO_PRIORITIZED_CODER_LIST = "prioritized_coder_list"
    INDEX_INFO_CODER_ID_TO_PRIORITY_MAP = "coder_id_to_priority_map"
    
    # debug flag
    DEBUG = False
    
    
    #----------------------------------------------------------------------------
    # instance methods
    #----------------------------------------------------------------------------


    def __init__( self, *args, **kwargs ):
        
        # ! ==> call parent's __init__()
        super( IndexInfo, self ).__init__()

        # ! ==> declare instance variables
        
        # basics - what index am I?
        self.m_index = -1
        
        # master map of coder ID to coder index info.
        self.m_coder_id_to_info_map = {}
        
        # coder quick reference lookup tables.
        self.m_coder_id_to_instance_map = {}
        self.m_coder_id_to_priority_map = {}
        self.m_prioritized_coder_list = []
                
        # exception helper
        self.m_exception_helper = ExceptionHelper()
        self.m_exception_helper.set_logger_name( self.LOGGER_NAME )
        self.m_exception_helper.logger_debug_flag = True
        self.m_exception_helper.logger_also_print_flag = False
        
        # debug variables
        self.m_debug_output_json_file_path = ""
        
    #-- END method __init__() --#
    

    def __str__( self ):
        
        # return reference
        string_OUT = ""
        
        # declare variables
        current_value = None
        current_value_label = None
        field_output_list = []
        
        current_value = self.get_index()
        current_value_label = "index"
        if ( current_value is not None ):
        
            field_output_list.append( str( current_value_label ) + ": " + str( current_value ) )
            
        #-- END check to see if coder_user_id --#

        current_value = self.get_coder_id_to_info_map()
        current_value_label = "coder info"
        if ( current_value is not None ):
        
            field_output_list.append( str( current_value_label ) + ": " + str( current_value ) )
            
        #-- END check to see if coder_user_id --#

        # DEBUG?
        if ( self.DEBUG == True ):

            current_value = self.get_coder_id_to_instance_map()
            current_value_label = "id-to-instance-info"
            if ( current_value is not None ):
            
                field_output_list.append( str( current_value_label ) + ": " + str( current_value ) )
                
            #-- END check to see if coder_user_id --#
    
            current_value = self.get_coder_id_to_priority_map()
            current_value_label = "id-to-priority"
            if ( current_value is not None ):
            
                field_output_list.append( str( current_value_label ) + ": " + str( current_value ) )
                
            #-- END check to see if coder_user_id --#
            
            current_value = self.get_prioritized_coder_list( rebuild_IN = True )
            current_value_label = "get_prioritized_coder_list()"
            if ( current_value is not None ):
            
                field_output_list.append( str( current_value_label ) + ": " + str( current_value ) )
                
            #-- END check to see if coder_user_id --#
            
            current_value = self.get_prioritized_coder_id_list( rebuild_IN = True )
            current_value_label = "get_prioritized_coder_id_list()"
            if ( current_value is not None ):
            
                field_output_list.append( str( current_value_label ) + ": " + str( current_value ) )
                
            #-- END check to see if coder_user_id --#
            
            current_value = self.get_coders_for_index( rebuild_IN = True )
            current_value_label = "get_coders_for_index()"
            if ( current_value is not None ):
            
                field_output_list.append( str( current_value_label ) + ": " + str( current_value ) )
                
            #-- END check to see if coder_user_id --#
            
            current_value = self.get_coder_for_index()
            current_value_label = "get_coder_for_index()"
            if ( current_value is not None ):
            
                field_output_list.append( str( current_value_label ) + ": " + str( current_value ) )
                
            #-- END check to see if coder_user_id --#
            
        #-- END check to see if debug --#

        # convert output list to string
        string_OUT = "\n====>".join( field_output_list )
                
        return string_OUT
        
    #-- END method __str__() --#


    def add_coder( self, coder_id_IN, priority_IN = None, *args, **kwargs ):
        
        '''
        Accepts a coder ID and an optional priority.  Updates all the stuff in
            this instance to make sure the coder is added correctly, Including:
            - create and store CoderIndexInfo instance.
            - map coder's User ID to their User instance.
            - map coder's User ID to their priority.
        '''
        
        # return reference
        status_OUT = ""
        
        # declare variables
        coder_index_info = None
        coder_id_to_info_dict = None
        coder_user_id = -1
        coder_index = -1
        coder_user = None
        coder_priority = None
        is_priority_valid = False
        priority_status = None
        
        # get maps from instance
        coder_id_to_info_dict = self.get_coder_id_to_info_map()
        
        # init from input parameters.
        coder_user_id = coder_id_IN
        coder_user = None
        coder_index = self.get_index()
        coder_priority = priority_IN
        
        # got ID?
        if ( ( coder_user_id is not None ) and ( int( coder_user_id ) > 0 ) ):
        
            # got index?
            if ( ( coder_index is not None ) and ( int( coder_index ) > 0 ) ):
            
                # yes.  Lookup the user.
                coder_user = User.objects.get( id = coder_user_id )
                
                # Make CoderIndexInfo instance.
                coder_index_info = CoderIndexInfo( coder_user_id_IN = coder_user_id,
                                                   coder_instance_IN = coder_user,
                                                   index_IN = coder_index,
                                                   priority_IN = coder_priority )
                
                # add it to map of coder ID to info for current index.
                coder_id_to_info_dict[ coder_user_id ] = coder_index_info
                
                # always rebuild other derived index information.
                self.build_index_info()

            else:
            
                # no index - broken.
                status_OUT = "No index - can't associate user with no index."
            
            #-- END check to see if index present. --#
        
        else:
        
            # no coder ID - broken.
            status_OUT = "No coder ID - can't associate user if no user."
        
        #-- END check to see if valid ID. --#

        return status_OUT        
        
    #-- END method add_coder_at_index() --#
    

    def build_index_info( self, *args, **kwargs ):
        
        '''
        Creates any helpful derived information on this index that can be used
            for processing.  Includes:
            - self.m_coder_id_to_instance_map - map coder's User ID to their User instance.
            - self.m_coder_id_to_priority_map - map coder's User ID to their priority.
            - self.m_prioritized_coder_list - list of coder User instances for this
                index, in order of their priority, from highest to lowest.
            
        Postconditions: Returns StatusContainer.
        '''
        
        # return reference
        status_OUT = StatusContainer()
        
        # declare variables
        me = "build_index_info"
        status_message = None
        coder_info_map = None
        coder_user_id = None
        coder_info = None
        coder_list = None
        
        # init status
        status_OUT.set_status_code( StatusContainer.STATUS_CODE_SUCCESS )
        
        # ! ----> clear out existing derived information
        self.m_coder_id_to_instance_map = {}
        self.m_coder_id_to_priority_map = {}
        self.m_prioritized_coder_list = None
        
        # ! ----> loop over coder info records.
        coder_info_map = self.get_coder_id_to_info_map()
        for coder_user_id, coder_info in six.iteritems( coder_info_map ):
        
            # update information for current coder:
            # ! --------> update id-to-instance map
            # ! --------> update id-to-priority map
            self.update_index_info_for_coder( coder_info )
            
        #-- END loop over coder info records --#
                
        # ! ----> rebuild coder list for index.
        coder_list = self.get_prioritized_coder_list( rebuild_IN = True )
        
        return status_OUT
        
    #-- END method build_index_info() --#


    def get_coder_for_article( self, article_IN, *args, **kwargs ):

        '''
        Accepts an article for which we want to pick a coder.  Returns User
            instance of coder to use for the current article.
            
        Specifically, for a given article, get index info, and then for each
            index with coders, go through the prioritized list of coders and use
            the first that has an Article_Data in the current article.
            
        Preconditions: This object needs to have been configured with at least
            one coder assigned to an index.
            
        Postconditions: returns User instance of coder for current article.
        '''

        # return reference
        coder_OUT = None

        # declare variables - coding processing.
        me = "get_coder_for_article"
        my_logger = None
        article_data_qs = None
        current_index_coder_list = None
        current_coder = None
        found_coder_for_index = False
        coder_article_data_qs = None
        article_data_count = -1
        
        # init logger
        my_logger = self.m_exception_helper        
        
        # article
        if ( article_IN is not None ):
    
            # retrieve the Article_Data QuerySet.    
            article_data_qs = article_IN.article_data_set.all()

            # For a given article, go through the prioritized list of coders and
            #     use the first that has an Article_Data in the current article.
            current_index_coder_list = self.get_prioritized_coder_list()
                
            if ( ( current_index_coder_list is not None )
                and ( isinstance( current_index_coder_list, list ) == True )
                and ( len( current_index_coder_list ) > 0 ) ):
            
                # we have a list.  Loop over coders to find first with
                #     Article_Data for this article.
                found_coder_for_index = False
                for current_coder in current_index_coder_list:
                
                    # coder already found?
                    if ( found_coder_for_index == False ):
        
                        # is there an article data by this coder for this
                        #     article?
                        try:
                        
                            # do a get() where coder = current_coder
                            coder_article_data_qs = article_data_qs.filter( coder = current_coder )
                            
                            # how many?
                            article_data_count = coder_article_data_qs.count()
                            
                            # 1 or more?
                            if ( article_data_count > 0 ):

                                # got at least one.  Return this coder!
                                coder_OUT = current_coder

                                # ...and set found flag to True to avoid
                                #     more lookups.
                                found_coder_for_index = True
                                
                                # and, finally, call get(), so we can log
                                #     if there are weird errors.
                                article_data = coder_article_data_qs.get()
                                
                            #-- END check to see if anything returned. --#
                            
                        except Article_Data.DoesNotExist as ad_dne:
                        
                            # No match.  This is unexpected.  Log and move on?
                            logging_message = "In " + me + "(): Article_Data.DoesNotExist caught, but after finding 1 or more Article_Data for the coder in question.  Something serious ain't right here.  Index = " + str( self.get_index() ) + "; current_coder = " + str( current_coder ) + "."
                            my_logger.process_exception( ad_dne, message_IN = logging_message )
                            
                        except Article_Data.MultipleObjectsReturned as ad_mor:
                        
                            # multiple Article_Data.  Hmmm...  Output a log
                            #     message and move on.
                            logging_message = "In " + me + "(): Article_Data.MultipleObjectsReturned caught - coder should have updated coding, rather than creating multiple.  Something ain't right here.  Index = " + str( self.get_index() ) + "; current_coder = " + str( current_coder ) + "."
                            my_logger.process_exception( ad_mor, message_IN = logging_message )
                        
                        except Exception as e:
                        
                            # unexpected exception caught.  Log a message
                            #     and move on.
                            logging_message = "In " + me + "(): unexpected Exception caught.  Something definitely ain't right here.  Index = " + str( self.get_index() ) + "; current_coder = " + str( current_coder ) + "."
                            my_logger.process_exception( e, message_IN = logging_message )
                        
                        #-- END try-except to see if we have a coder --#
                        
                    #-- END check to see if already found a coder for this index --#
        
                #-- END loop over this index's coder list --#
                        
            else:
            
                # no coder list.  Log a message, omit this index, and
                #     move on.
                logging_message = "No coders for index " + str( self.get_index() ) + ".  Moving on."
                my_logger.output_debug_message( logging_message, method_IN = me  )
                
            #-- END check to see if there is a coder list. --#
            
        #-- END check to see if article actually passed in. --#
        
        return coder_OUT
        
    #-- END method get_coder_for_article() --#


    def get_coder_for_index( self ):
        
        '''
        Accepts a coder index.  Uses it to get ID of coder associated with that
           index with the highest priority, and returns instance of that User.
           If none found, returns None.
        '''
        
        # return reference
        instance_OUT = None
        
        # declare variables
        coder_list = None
        coder_count = -1

        # get list of coders for current index
        coder_list = self.get_coders_for_index()
        
        # got anything?
        coder_count = len( coder_list )
        
        if ( coder_count > 0 ):
        
            # yes.  Return first item in the list (it is in priority order, from
            #     highest priority to lowest).
            instance_OUT = coder_list[ 0 ]
            print( "++++ found User: "******"get_coder_priority"
        coder_id_to_info_dict = None
        coder_info = None
        coder_priority = -1

        # retrieve the map of coder IDs to priorities.
        coder_id_to_info_dict = self.get_coder_id_to_info_map()
        
        # retrieve priority from there.
        coder_info = coder_id_to_info_dict.get( coder_id_IN, None )
        coder_priority = coder_info.get_priority()
            
        # ! got a priority?
        if ( ( coder_priority is None )
            or ( isinstance( coder_priority, six.integer_types ) == False )
            or ( coder_priority < 0 ) ):
                
            # no.  Return Default.
            coder_priority = default_priority_IN

        #-- END check to see if we got a priority based on index. --#
        
        # store coder_priority in value_OUT.
        value_OUT = coder_priority
                
        return value_OUT        
        
    #-- END method get_coder_priority() --#
    
        
    def get_coders_for_index( self, rebuild_IN = False ):
        
        '''
        Retrieves list of User instances of coders associated with this index,
            in priority order, highest first to lowest last.  If none found,
            returns empty list.
            
        Postconditions: If there are two coders with the same priority, they
            will be together in the list in the appropriate position for their
            priority, but arbitrarily ordered from invocation to invocation (so
            in no particular order).  You've been warned.  If you care, don't
            assign two coders the same priority, and/or don't have multiple
            coders assigned to a given index with no priorities.
        '''
        
        # return reference
        coder_list_OUT = []
        
        # declare variables
        me = "get_coders_for_index"

        # get value from instance.
        coder_list_OUT = self.get_prioritized_coder_list( rebuild_IN = rebuild_IN )
                
        return coder_list_OUT        
        
    #-- END method get_coders_for_index() --#
        
        
    def get_index( self ):
        
        '''
        Retrieves and returns the index from the current instance.
        '''
        
        # return reference
        value_OUT = None
        
        # declare variables
        me = "get_index"
        
        value_OUT = self.m_index
        
        return value_OUT        
        
    #-- END method get_index() --#
    
        
    def get_prioritized_coder_id_list( self, rebuild_IN = False ):
        
        '''
        Retrieves list of User IDs of coders associated with this index,
            in priority order, highest first to lowest last.  If none found,
            returns empty list.
            
        Postconditions: If there are two coders with the same priority, they
            will be together in the list in the appropriate position for their
            priority, but arbitrarily ordered from invocation to invocation (so
            in no particular order).  You've been warned.  If you care, don't
            assign two coders the same priority, and/or don't have multiple
            coders assigned to a given index with no priorities.
        '''
        
        # return reference
        coder_id_list_OUT = []
        
        # declare variables
        me = "get_prioritized_coder_id_list"
        status_message = ""
        prioritized_coder_list = None
        current_coder = None
        current_coder_id = None
        
        # get prioritized coder/User instance list.
        prioritized_coder_list = self.get_prioritized_coder_list( rebuild_IN = rebuild_IN )
        
        # loop, grabbing ID from each instance and adding it to output list.
        for current_coder in prioritized_coder_list:
        
            # get id.
            current_coder_id = current_coder.id
            
            # add to the list.
            coder_id_list_OUT.append( current_coder_id )
            
        #-- END loop over coders. --#
                
        return coder_id_list_OUT        
        
    #-- END method get_prioritized_coder_id_list() --#
        
        
    def get_prioritized_coder_list( self, rebuild_IN = False ):
        
        '''
        Retrieves list of User instances of coders associated with this index,
            in priority order, highest first to lowest last.  If none found,
            returns empty list.
            
        Postconditions: If there are two coders with the same priority, they
            will be together in the list in the appropriate position for their
            priority, but arbitrarily ordered from invocation to invocation (so
            in no particular order).  You've been warned.  If you care, don't
            assign two coders the same priority, and/or don't have multiple
            coders assigned to a given index with no priorities.
        '''
        
        # return reference
        coder_list_OUT = []
        
        # declare variables
        me = "get_prioritized_coder_list"
        debug_message = ""
        coder_id_to_info_dict = None
        
        # declare variables - organize users
        coder_priority_to_user_list_map = {}
        id_count = -1
        coder_user_id = -1
        coder_info = -1
        coder_priority = -1
        coder_user_instance = None
        priority_user_list = None
        priority_key_list = None
        priority_key = None
        
        # get value from instance.
        coder_list_OUT = self.m_prioritized_coder_list
        
        # Do we need to build/rebuild?
        if ( ( coder_list_OUT is None ) or ( rebuild_IN == True ) ):
        
            # ! ==> Build/Rebuild!
            
            # Get map of coder user ID to info from instance
            coder_id_to_info_dict = self.get_coder_id_to_info_map()
    
            # loop over coders to build priority-to-user list.
            coder_priority_to_user_list_map = {}
            for coder_user_id, coder_info in six.iteritems( coder_id_to_info_dict ):
                
                # get priority.
                coder_priority = coder_info.get_priority()
                
                # get instance for user.
                coder_user_instance = coder_info.get_coder_user_instance()
                
                # see if there is a user list for the current priority.
                if ( coder_priority not in coder_priority_to_user_list_map ):
                
                    # not yet.  Add one.
                    coder_priority_to_user_list_map[ coder_priority ] = []
                    
                #-- END check to see if list already in priority-to-user map --#
                
                # get list for current priority
                priority_user_list = coder_priority_to_user_list_map.get( coder_priority, None )
                
                # and append user to list.
                priority_user_list.append( coder_user_instance )
            
            #-- END loop over coders --#
                
            # get list of priorities (keys in the map)...
            priority_key_list = list( six.viewkeys( coder_priority_to_user_list_map ) )
            
            # ...and sort them in reverse order (largest first, to smallest).
            priority_key_list.sort( reverse = True )
            
            # for each priority, retrieve user list and combine it with the
            #     output list.
            coder_list_OUT = []
            for priority_key in priority_key_list:
            
                # get priority_user_list
                priority_user_list = coder_priority_to_user_list_map.get( priority_key, None )
                
                # got anything?
                if ( ( priority_user_list is not None )
                    and ( isinstance( priority_user_list, list ) == True )
                    and ( len( priority_user_list ) > 0 ) ):
                    
                    # yes, got one.  Add its values to the right end of the
                    #     list.
                    coder_list_OUT.extend( priority_user_list )
                
                #-- END check to see if user list for this priority --#
    
            #-- END loop over priorities. --#
            
            # store the list in this instance.
            self.set_prioritized_coder_list( coder_list_OUT )
            coder_list_OUT = self.get_prioritized_coder_list( rebuild_IN = False )
            
        #-- END check to see if we need to build/rebuild --#
        
        return coder_list_OUT        
        
    #-- END method get_prioritized_coder_list() --#
        
        
    def set_coder_priority( self, coder_id_IN, priority_IN, *args, **kwargs ):
        
        '''
        Accepts a coder User ID, an integer priority for that coder.  Checks to
            make sure coder ID and priority are passed in.  If so, adds entry to
            map of coder IDs to priorities for the coder.
            
        Returns StatusContainer instance.
        '''
        
        # return reference
        status_OUT = StatusContainer()
        
        # declare variables
        me = "set_coder_priority"
        coder_to_priority_dict = {}
        coder_index = -1
        index_priority_map = {}
        status_message = ""
        
        # init status
        status_OUT.set_status_code( StatusContainer.STATUS_CODE_SUCCESS )
        
        # got a coder ID?
        if ( ( coder_id_IN is not None )
            and ( isinstance( coder_id_IN, six.integer_types ) == True )
            and ( coder_id_IN > 0 ) ):
            
            # got a coder ID - got a priority?
            if ( ( priority_IN is not None )
                and ( isinstance( priority_IN, six.integer_types ) == True )
                and ( priority_IN > 0 ) ):
                
                # add to coder_to_priority_dict
                coder_to_priority_dict = self.get_coder_id_to_priority_map()
                coder_to_priority_dict[ coder_id_IN ] = priority_IN
                            
            else:
            
                # ERROR - must have a priority passed in.
                status_OUT.set_status_code( StatusContainer.STATUS_CODE_ERROR )
                status_message = "In " + me + ": No coder priority passed in ( value = \"" + str( priority_IN ) + "\" ), so can't set priority."
                status_OUT.add_message( status_message )
                
            #-- END check to see if priority passed in --#
            
        else:
        
            # ERROR - must have a coder ID passed in.
            status_OUT.set_status_code( StatusContainer.STATUS_CODE_ERROR )
            status_message = "In " + me + ": No coder User ID passed in ( value = \"" + str( coder_id_IN ) + "\" ), so can't set priority."
            status_OUT.add_message( status_message )
        
        #-- END check to see if coder User ID passed in. --#
        
        return status_OUT
    
    #-- END method set_coder_priority() --#


    def set_index( self, value_IN ):
        
        '''
        Accepts an index value.  Stores it in nested instance variable.
            Returns stored value.
        '''
        
        # return reference
        value_OUT = None
        
        # declare variables
        me = "set_index"
        
        # store the value
        self.m_index = value_IN
        
        # retrieve the value
        value_OUT = self.get_index()
        
        return value_OUT
    
    #-- END method set_index() --#


    def set_prioritized_coder_list( self, value_IN ):
        
        '''
        Accepts a prioritized list of coders.  Stores it in nested instance
            variable.  Returns stored value.
        '''
        
        # return reference
        value_OUT = None
        
        # declare variables
        me = "set_prioritized_coder_list"
        
        # store the value
        self.m_prioritized_coder_list = value_IN
        
        # retrieve the value
        value_OUT = self.get_prioritized_coder_list()
        
        return value_OUT
    
    #-- END method set_prioritized_coder_list() --#
    
    
    def update_index_info_for_coder( self, coder_info_IN, *args, **kwargs ):
        
        '''
        Accepts a CoderIndexInfo instance.  Updates all the stuff in this
            instance to make sure the coder is added correctly, Including:
            - map coder's User ID to their User instance.
            - map coder's User ID to their priority.
            
        Preconditions: called as part of self.build_index_info(), so this does
            not call that.
        '''
        
        # return reference
        status_OUT = ""
        
        # declare variables
        coder_index_info = None
        coder_id_to_instance_dict = {}
        coder_user_id = -1
        coder_index = -1
        coder_user = None
        coder_priority = None
        is_priority_valid = False
        priority_status = None
        
        # get maps from instance
        coder_id_to_instance_dict = self.get_coder_id_to_instance_map()
        
        # got info?
        if ( coder_info_IN is not None ):
        
            # yes - get info on user from it.
            coder_index_info = coder_info_IN
        
            # init from info.
            coder_user_id = coder_index_info.get_coder_user_id()
            coder_user = coder_index_info.get_coder_user_instance()
            coder_index = coder_index_info.get_index()
            coder_priority = coder_index_info.get_priority()
            
            # got ID?
            if ( ( coder_user_id is not None ) and ( int( coder_user_id ) > 0 ) ):
            
                # got index?
                if ( ( coder_index is not None ) and ( int( coder_index ) > 0 ) ):
                
                    # Set all the things up internally.
                    coder_id_to_instance_dict[ coder_user_id ] = coder_user
                    
                    # set priority?
                    is_priority_valid = IntegerHelper.is_valid_integer( coder_priority, must_be_greater_than_IN = -1 )
                    if ( is_priority_valid == True ):
    
                        # priority value is valid - set priority.
                        priority_status = self.set_coder_priority( coder_user_id, coder_priority )
                        
                    else:
                    
                        # not valid.  Set coder_priority to None.
                        coder_priority = None
                        
                    #-- END check to see if priority value is valid --#
                                    
                    # rebuild other derived index information.
                    #self.build_index_info()
    
                else:
                
                    # no index - broken.
                    status_OUT = "No index - can't associate user with no index."
                
                #-- END check to see if index present. --#
            
            else:
            
                # no coder ID - broken.
                status_OUT = "No coder ID - can't associate user if no user."
            
            #-- END check to see if valid ID. --#
            
        else:
        
            # no info passed in, nothing to do.
            status_OUT = "No coder info passed in, nothing to do."
            
        #-- END check to see if coder info. --#

        return status_OUT        
class CoderIndexInfo( object ):
    
    
    #----------------------------------------------------------------------
    # constants-ish
    #----------------------------------------------------------------------    


    # Logger name
    LOGGER_NAME = "context_analysis.reliability.coder_index_info"
        
    
    #----------------------------------------------------------------------------
    # instance methods
    #----------------------------------------------------------------------------


    def __init__( self,
                  coder_user_id_IN = None,
                  coder_user_instance_IN = None,
                  index_IN = None,
                  priority_IN = None,
                  *args,
                  **kwargs ):
        
        # ! ==> call parent's __init__()
        super( CoderIndexInfo, self ).__init__()

        # ! ==> declare instance variables
        
        # coder info. init.
        self.m_coder_user_id = None
        self.m_coder_user_instance = None
        self.m_index = None
        self.m_priority = None        
        
        # set coder info.
        self.set_coder_user_id( coder_user_id_IN )
        self.set_coder_user_instance( coder_user_instance_IN )
        self.set_index( index_IN )
        self.set_priority( priority_IN )

        # exception helper
        self.m_exception_helper = ExceptionHelper()
        self.m_exception_helper.set_logger_name( self.LOGGER_NAME )
        self.m_exception_helper.logger_debug_flag = True
        self.m_exception_helper.logger_also_print_flag = False
        
    #-- END method __init__() --#
    
        
    def __str__( self ):
        
        # return reference
        string_OUT = ""
        
        # declare variables
        current_value = None
        current_value_label = None
        field_output_list = []
        
        current_value = self.get_coder_user_id()
        current_value_label = "user ID"
        if ( current_value is not None ):
        
            field_output_list.append( str( current_value_label ) + ": " + str( current_value ) )
            
        #-- END check to see if coder_user_id --#

        current_value = self.get_coder_user_instance()
        current_value_label = "user instance"
        if ( current_value is not None ):
        
            field_output_list.append( str( current_value_label ) + ": " + str( current_value ) )
            
        #-- END check to see if coder_user_id --#

        current_value = self.get_index()
        current_value_label = "index"
        if ( current_value is not None ):
        
            field_output_list.append( str( current_value_label ) + ": " + str( current_value ) )
            
        #-- END check to see if coder_user_id --#

        current_value = self.get_priority()
        current_value_label = "priority"
        if ( current_value is not None ):
        
            field_output_list.append( str( current_value_label ) + ": " + str( current_value ) )
            
        #-- END check to see if coder_user_id --#

        # convert output list to string
        string_OUT = ", ".join( field_output_list )
                
        return string_OUT
        
    #-- END method __str__() --#


    def get_coder_user_id( self ):
        
        '''
        Retrieves and returns the User ID from the current instance.
        '''
        
        # return reference
        value_OUT = None
        
        # declare variables
        me = "get_coder_user_id"
        
        value_OUT = self.m_coder_user_id
        
        return value_OUT        
        
    #-- END method get_coder_user_id() --#
    
        
    def get_coder_user_instance( self ):
        
        '''
        Retrieves and returns the User instance from the current instance.  If
            None set yet, tries to get ID, then retrieve instance for current
            ID.
            
        Postconditions:  If no ID, returns None.  If no instance for ID, throws
            exception.  Other errors return None.
        '''
        
        # return reference
        value_OUT = None
        
        # declare variables
        me = "get_coder_user_instance"
        coder_user_id = None
        coder_user_instance = None
        
        value_OUT = self.m_coder_user_instance
        
        # got anything?
        if ( value_OUT is None ):
        
            # No instance.  Got an id?
            coder_user_id = self.get_coder_user_id()
            if ( ( coder_user_id is not None ) and ( coder_user_id > 0 ) ):
            
                # We have an ID.  Look up the user instance.
                coder_user_instance = User.objects.get( id = coder_user_id )
                
                # store the result.
                self.set_coder_user_instance( coder_user_instance )
            
            #-- END check to see if we have a user ID. --#
            
            # try again.
            value_OUT = self.get_coder_user_instance()
        
        #-- END check to see if instance there already. --#
        
        return value_OUT        
        
    #-- END method get_coder_user_instance() --#
    
        
    def get_index( self ):
        
        '''
        Retrieves and returns the index from the current instance.
        '''
        
        # return reference
        value_OUT = None
        
        # declare variables
        me = "get_index"
        
        value_OUT = self.m_index
        
        return value_OUT        
        
    #-- END method get_index() --#
    
        
    def get_priority( self ):
        
        '''
        Retrieves and returns the priority from the current instance.
        '''
        
        # return reference
        value_OUT = None
        
        # declare variables
        me = "get_priority"
        
        value_OUT = self.m_priority
        
        return value_OUT        
        
    #-- END method get_priority() --#
    
        
    def set_coder_user_id( self, value_IN ):
        
        '''
        Accepts a coder user id value.  Stores it in nested instance variable.
            Returns stored value.
        '''
        
        # return reference
        value_OUT = None
        
        # declare variables
        me = "set_coder_user_id"
        temp_user_instance = None
        
        # store the value
        self.m_coder_user_id = value_IN
        
        # retrieve the value
        value_OUT = self.get_coder_user_id()
        
        # try to get User instance, which looks us User and loads it into
        #     this instance, as well.
        temp_user_instance = self.get_coder_user_instance()
        
        return value_OUT
    
    #-- END method set_coder_user_id() --#


    def set_coder_user_instance( self, value_IN ):
        
        '''
        Accepts a coder user instance.  Stores it in nested instance variable.
            Returns stored value.
        '''
        
        # return reference
        value_OUT = None
        
        # declare variables
        me = "set_coder_user_instance"
        
        # store the value
        self.m_coder_user_instance = value_IN
        
        # retrieve the value
        value_OUT = self.get_coder_user_instance()
        
        return value_OUT
    
    #-- END method set_coder_user_instance() --#


    def set_index( self, value_IN ):
        
        '''
        Accepts an index value.  Stores it in nested instance variable.
            Returns stored value.
        '''
        
        # return reference
        value_OUT = None
        
        # declare variables
        me = "set_index"
        
        # store the value
        self.m_index = value_IN
        
        # retrieve the value
        value_OUT = self.get_index()
        
        return value_OUT
    
    #-- END method set_index() --#


    def set_priority( self, value_IN ):
        
        '''
        Accepts a priority value.  Stores it in nested instance variable.
            Returns stored value.
        '''
        
        # return reference
        value_OUT = None
        
        # declare variables
        me = "set_priority"
        
        # store the value
        self.m_priority = value_IN
        
        # retrieve the value
        value_OUT = self.get_priority()
        
        return value_OUT