def get_macro_obj_from_id(macro_id, macro=None): '''Get a processed macro from the macro id. Raises an exception on error. Assumes valid input. Incrments the view counter for this macro.''' # First check memcache for a processed macro object: macro_obj = memcache.get(MACRO_PROC_KEY % macro_id) if not macro_obj: # Nothing from memcache, load from data store. if not macro: saved_macro_entity = SavedMacroOps.get_macro_entity(macro_id) # If saved_macro_entity is still none, we failed # in the datastore. if saved_macro_entity is None: raise NoInputError("Macro id '%s' not found." % macro_id) macro = saved_macro_entity.macro # Process the macro, lazily importing. from macro.interpret.interpreter import MacroInterpreter macro_obj = MacroInterpreter().interpret_macro(macro) # Save macro in memcached. memcache.add(MACRO_PROC_KEY % macro_id, macro_obj, MEMCACHED_MACRO_PROC) return macro_obj
def generate_search_page(path, terms, page, sort, page_size=DEF_SEARCH_RESULTS): ''' Generate a search results page. ''' error = None # Make sure page is a number, failing on bad input prev_page = None if not page: page = 1 else: page = int(page) prev_page = page - 1 # Do the search # TODO: Add column sort results = [] is_next_page = False if (len(terms) < SINGLE_TAG_MAX_LENGTH): (results, is_next_page) = SavedMacroOps.search(terms, page=page, num=page_size, sort=sort) else: error = "Query term too long." terms = terms[:SINGLE_TAG_MAX_LENGTH] + "..." # If the number of results is less than that of page_size, # then there is no next page. next_page = None if is_next_page: next_page = page + 1 # If there are no results, add an error. if not error and len(results) == 0: error = "No results found." # TODO: Hook up template controls to sort results. # TODO: Hook up template controls to page forward/back. # Return generated search page. return render_template('base.template', {'query' : terms, 'content': render_template('search.template', {'search_error' : error, 'curr_version' : "%s.%s.%s" % (MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION), 'query' : terms, 'q_esc' : FORM_QUERY_ESC, 'results' : results, 'sort' : sort, 'page_var' : FORM_SEARCH_PAGE, # Only give a prev page if we're over page 1. 'prev_page' : prev_page, 'page' : page, 'next_page' : next_page, }, path)}, path)
def __do_rating(self, macro_id, rating): ''' Helper function to do the rating. ''' # Increment macro rating, and return the # rating, rounded to the nearest half-star. saved_macro = SavedMacroOps(macro_id) return saved_macro.add_rating(rating)
def generate_view_page(path, macro_id, host_url, save_values={}): ''' Generate a macro view page. Returns None on error. Propogates exceptions up. ''' ret_page = None # Make sure we got valid input. if not macro_id: raise NoInputError("You entered an invalid macro ID. Try again?") # Ensure we have a saved macro. Will throw exception on fail. saved_macro = SavedMacroOps(macro_id) # View pages are extremely heavy, and the majority of their # data doesn't change. Use helper to handle this. macro_form_template_values = get_view_dict_from_macro_id(macro_id, saved_macro) # Add in edit and view links. # Add in the author link. # Add in the form ids. page_values = {'macro_link' : '%s/%s' % (host_url, macro_id), 'macro_edit' : '%s?%s=%s' % (host_url, GET_MACRO_LINK, macro_id), 'author' : _format_author(saved_macro.entity.name, saved_macro.entity.server), 'send_email' : FORM_MACRO_EMAIL, 'send_input_form' : FORM_EMAIL_INPUT, 'to' : FORM_EMAIL_TO, 'from' : FORM_EMAIL_FROM, } macro_form_template_values.update(page_values) # Generate the dynamic parts of the page: # 1. Increment the view counter on this macro. # 2. Get the rating in terms of stars for this macro. dynamic_page_vals = { 'stars' : [i + 1 for i in range(MAX_RATING)], 'num_rates' : saved_macro.entity.num_rates, 'views' : saved_macro.add_to_view_count(), 'rating' : saved_macro.get_rating_dict(saved_macro.get_rating()), } macro_form_template_values.update(dynamic_page_vals) # Is this another save attempt after errors? If so, update # with errors and previous values. macro_form_template_values.update(save_values) # Call helper to interpret the macro behind the id. macro_obj = get_macro_obj_from_id(macro_id, saved_macro.entity.macro) # Populate the templace with the processed macro. macro_form_template_values['processed_macro_html'] = \ render_macro(macro_obj, path) # Get the copy and paste form of the macro macro_form_template_values['copy_paste'] = \ _render_copy_cmd(macro_obj) # Render the macro view template html. ret_page = render_template('view.template', macro_form_template_values, path) return render_template('base.template', {'content': ret_page}, path)
def __render_form_page(self, macro): # Validate form errors and save form_errors = {} opt_fields = { 'title_show': 'none', 'title_hide': 'block', 'notes_show': 'none', 'notes_hide': 'block', } # Title is optional. title = self.request.get(FORM_SAVE_TITLE, default_value="")[0:TITLE_MAX_LENGTH] # Author is optional. name = self.request.get(FORM_SAVE_NAME, default_value="")[0:NAME_MAX_LENGTH] # Server is optional. server = self.request.get(FORM_SAVE_SERVER, default_value="")[0:SERVER_MAX_LENGTH] # Notes are optional notes = self.request.get(FORM_SAVE_NOTES, default_value="")[0:NOTES_TEXT_LENGTH] # Update optional field status if valid(title) or valid(name) or valid(server): opt_fields['title_show'] = "block" opt_fields['title_hide'] = "none" if valid(notes): opt_fields['notes_show'] = "block" opt_fields['notes_hide'] = "none" # Version defaults to current version. version = "%s.%s.%s" % (MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION) # Must have at least one class, and all must be recognized. classes = [] for c in CLASS_LIST: if self.request.get(c.replace(" ", "_")) == '1': classes.append(c) if len(classes) == 0: form_errors['class_error'] = get_form_error(FORM_SAVE_CLASSES) # Must have at least one tag, and all must be recognized. # De-dup tags via a set. tags = list(set([t for t in re.split("\s*,\s*", self.request.get(FORM_SAVE_TAGS)) if t])) if len(tags) == 0: form_errors['tag_error'] = get_form_error(FORM_SAVE_TAGS) else: if len(self.request.get(FORM_SAVE_TAGS)) > ALL_TAGS_MAX_LENGTH: form_errors['tag_error'] = "Too many tags!" else: longest_tag = max(tags, key=len) if len(longest_tag) > SINGLE_TAG_MAX_LENGTH: form_errors['tag_error'] = "Tag %s exceeds the max tag length of %s chars!" % (longest_tag, SINGLE_TAG_MAX_LENGTH) # Use memcached to throttle people to saving one macro every 10 # seconds. Only do this AFTER the user has fixed errors. if len(form_errors) == 0: secs_left = throttle_action("save", self.request.remote_addr) if secs_left > 0: form_errors['spam'] = "You must wait %s seconds before you may save another macro." % (secs_left) # Were there any errors? If success, save and redirect if len(form_errors) == 0: # Return an error page if something really wrong happens. link = None link = SavedMacroOps.save_macro(macro, cgi.escape(notes), title, name, classes, tags, version, server) # Redirect to the intepreter with the link. self.redirect("/%s" % link) # Error in validation--display. else: # Create template data based on input. input_vals = { 'title_data' : title, 'name_data' : name, 'macro_notes' : notes, 'curr_version' : version, 'note_limit' : NOTES_TEXT_LENGTH, 'note_ch_left' : NOTES_TEXT_LENGTH - len(notes), 'class_list' : translate_classmap(sel=set(classes)), # Server list lives in a template. 'server_list' : template.render(os.path.join(_TEMPLATE_PATH, 'servers.template'), {}), 'tag_def_list' : TAG_LIST, 'selected_server' : server, 'tag_list' : ",".join(tags), } # Add in errors. input_vals.update(form_errors) # Add in optional field status. input_vals.update(opt_fields) # Write out the page return generate_edit_page(_TEMPLATE_PATH, macro, save_values=input_vals)