def __import_search_state(self): """ Import the search terms that were used on previous page loads. Some of these terms may be prefixed with a #, which makes them either cardtypes or channel names. Output is either strings of search/filter terms, or None """ self.search = BaseState._find_state_variable(self, 'xs') # TODO: Must check each application's search state before turning on shuffle mode. # Python's recommended way to see if a string is empty or null is using the not operator. # This doesn't differentiate between None and an empty string '' though. if not self.search and isinstance(self.search, str): self.reshuffle = True else: self.reshuffle = False # First, check if any of the search terms should be processed as a # cardtype and be added to the filter state instead. if self.search is not None: searchterms = self.search.split(' ') searchterms = list(filter(None, searchterms)) # remove nulls [newfilters, removeterms ] = BaseState._process_search_strings(self, '#', searchterms) # Remove filter strings from the search state list if they exist [searchterms.remove(term) for term in removeterms] self.search = searchterms # Take off leading #-sigil for card type searches self.card_filter = list(map(lambda x: x[1:], newfilters)) for ctype in self.card_filter: getattr(self, ctype).filtertype = True if self.card_filter == []: self.card_filter = None
def __init__(self, in_state=None, env={}): BaseState.__init__(self, in_state, None) self.config = GlobalConfig self.headers = [] # Getting defaults from the other states requires us to first import # any random seed value. Then, we can finish setting the imported state self.__import_random_seed() self.__set_application_state_defaults() self.__import_state()
def __init__(self, in_state=None): # Open the config file, and set card type defaults per state variable BaseState.__init__(self, in_state, 'medusa.ini') # Process all state variables listed in medusa.ini self.__import_state() # Now that we've imported, shuffle any card types we want to shuffle for ctype in self.config.get("card_properties", "randomize").replace(" ", "").split(","): getattr(self, ctype).shuffle()
def __import_filtered_card_count(self): """ Filtered card count, tracked when we have a query type and a filter count and cards on previous pages were omitted from being displayed. Tracking this allows you to fix the page count to represent reality better. Output must be an integer. """ self.filtered = BaseState._find_state_variable(self, 'xx') if ((self.filtered is not None) and (self.search is not None) and (self.card_filter is not None)): self.filtered = BaseState._int_translate(self, self.filtered, 1, 0) else: self.filtered = 0
def __import_page_count_state(self): """ For all subsequent "infinite-scroll AJAX" content after the initial page load, we track the current page number of content. Output must be an integer. """ self.page = BaseState._find_state_variable(self, 'xp') # If page was read in as a special state variable, use that (for search results) if self.page is not None: self.page = BaseState._int_translate(self, self.page, 2, 0) else: self.page = 0
def export_state(self, cards, query_terms, filter_terms, filtered_count): """ Once all cards are read, calculate a new state variable to embed in the more-contents page link. """ # Start by calculating the distance from the next page for each # card type. This updates the state.ctype.distance values BaseState._calculate_last_distance(self, cards, common="news") # Finally, construct the state string for the next page export_parts = [ self.__export_card_state(), self.__export_search_state(query_terms), self.__export_filter_state(filter_terms), self.__export_filtered_card_count(filtered_count) ] export_parts = filter(None, export_parts) export_string = ':'.join(export_parts) return export_string
def __import_filter_state(self): """ This must run after the import_search_state! If no filter strings were found during search, we may need to process a set of filter strings that were excised out on a previous page load. """ if self.card_filter is None: self.card_filter = BaseState._find_state_variable(self, 'xo') if self.card_filter is not None: filterterms = self.card_filter.split(' ') # Add-filter-cardtypes expects strings that start with # hashtag_process = map(lambda x: "#" + x, filterterms) [newfilters, removeterms ] = BaseState._process_search_strings(self, '#', hashtag_process) # Take off leading #-sigil for card type searches self.card_filter = map(lambda x: x[1:], newfilters) # Record filters being set for ctype in self.card_filter: getattr(self, ctype).filtertype = True if self.card_filter == []: self.card_filter = None
def __import_theme_state(self): """ In the top-level state object, we track an appearance variable, which corresponds to the exact state variable imported (and exported) for the appearance of the entire Constantina site. The appearance value lets us look up which theme we display for the user. This theme value is a path fragment to a theme's images and stylesheets. """ # Look for appearance state in the QUERY_PARAMS appearance_state = BaseState._find_state_variable(self, 'xa') if appearance_state is not None: # Read in single char of theme state value self.appearance = BaseState._int_translate(self, appearance_state, 1, 0) # If the configuration supports a random theme, and we didn't have a # theme provided in the initial state, let's choose one randomly seed() # Enable non-seeded choice GlobalTheme.set(self.appearance) if self.seed: # Re-enable seeded nonrandom choice seed(self.seed) self.theme = GlobalTheme.theme
def __import_random_seed(self): """ Set the return seed based on a 14-digit string from the state variable. As an input to seed(), this has to be a float between zero and one. This seed is used to consistently seed the shuffle function, so that between page loads, we know the shuffled card functions give the same shuffle ordering. """ self.seed = BaseState._find_state_variable(self, "seed") if self.seed is None: self.seed = round(random(), 14) else: self.seed = float(str("0." + self.seed)) seed(self.seed) # Now the RNG is seeded with our consistent value
def __import_permalink_state(self): """ Any card type that can be displayed on its own is a permalink-type card, and will have state that describes which permalink page should be loaded. Output is either string (filename, as utime), or None. """ permalink_states = [ sv[0] for sv in self.config.items("special_states") if sv[1].find("permalink") != -1 ] for state in permalink_states: value = BaseState._find_state_variable(self, state) if value != None: attrib = self.config.get("special_states", state) setattr(self, attrib, value) return # Only one permalink state per page. First one takes precedence
def __import_card_state(self): """ News cards and other content cards' state is tracked here. Seed tracking between page loads means we don't need to log which content cards were shown on a previous page. However, to preserve card spacing rules, we do need to track the distance of each card type from the first element of the current page. Output is an integer or None. """ # For each content card type, populate the state variables # as necessary. for state_var in [s[0] for s in self.config.options('card_counts')]: # NOTE: This ctype attribute naming requires each content card type to # begin with a unique alphanumeric character. ctype = [ value for value in self.config.options("card_counts") if value[0] == state_var ][0] distance = BaseState._find_state_variable(self, state_var) getattr(self, ctype).distance = distance