Best Python code snippet using localstack_python
__init__.py
Source:__init__.py
1import collections2import configargparse3from flask import Flask, Response, send_from_directory4from flask_cors import CORS5import jinja26import json7import os8import pandas as pd9import pkg_resources10import pkgutil11import re12import requests13import shutil14import sys15from flipbook.utils import get_relative_directory_to_data_files_list, get_relative_directory_to_metadata, \16 is_excel_table, get_data_page_url, METADATA_JSON_FILE_TYPE, CONTENT_HTML_FILE_TYPE17PATH_COLUMN = 'Path'18WEBSITE_DIR = "flipbook_html"19MAIN_PAGE_HEADER_FILENAME = "flipbook_main_page_header.html"20DATA_PAGE_HEADER_FILENAME = "flipbook_data_page_header.html"21p = configargparse.ArgumentParser(22 formatter_class=configargparse.DefaultsFormatter,23 add_config_file_help=True,24 add_env_var_help=True,25 config_file_parser_class=configargparse.YAMLConfigFileParser,26 default_config_files=["~/.flipbook_config"],27 args_for_writing_out_config_file=["--save-current-options-to-config-file"],28)29p.add_argument("-i", "--include", action="append", help="Only include files whose path contains this keyword")30p.add_argument("-x", "--exclude", action="append", help="Skip files whose path contains this keyword. If both "31 " --include and --exclude are specified, --exclude takes precedence over --include", default=[WEBSITE_DIR])32p.add_argument("-t", "--form-responses-table", default="flipbook_form_responses.tsv",33 help="The .tsv or .xls path where form responses are saved. If the file already exists,"34 "it will be parsed for previous form responses and then updated as the user fills in the form(s)."35 "If the file doesn't exist, it will be created after the 1st form response.")36p.add_argument("-m", "--metadata-table", default="flipbook_metadata.tsv",37 help="The .tsv or .xls path containing metadata to show on data pages. There are two optional ways "38 "to add metadata to the data pages. The 1st way is to put a 'flipbook_metadata.json' file "39 "inside a directory that contains images or data files (in which case any key-value pairs from "40 "the json file will be shown at the top of the data page that displays those images). "41 "The other way is to specify this table, which needs to have a 'Path' column with relative "42 "directory paths that contain images and data files. The data page corresponding to those "43 "directory paths will then display values from the other columns in this table. If both this table "44 "and 'flipbook_metadata.json' files are found, the values from this table will override values in "45 "the 'flipbook_metadata.json' files.")46p.add_argument("-j", "--form-schema-json", help="Path of .json file containing a custom form schema. For the expected format "47 "see https://github.com/broadinstitute/flipbook/tree/main/form_schema_examples")48p.add_argument("-s", "--sort-by", action="append", help="Order pages by metadata column(s)")49p.add_argument("-r", "--reverse-sort", action="store_true", help="Reverses the sort order")50p.add_argument("--hide-metadata-on-home-page", action="store_true", help="Don't show metadata columns in the "51 "home page table")52p.add_argument("--add-metadata-to-form-responses-table", action="store_true", help="Also write metadata columns to the "53 "form responses table when saving users' form responses")54p.add_argument("--generate-static-website", action="store_true", help="Instead of starting a web server, this option "55 "causes FlipBook to write out a set of static html pages for all the images it finds and then exit. "56 "The generated pages can then be viewed in a browser, uploaded to some other web server (such as "57 "GitHub Pages, embedded in another existing website, etc. The generated web pages are identical to "58 "the standard FlipBook user interface except they don't contain the forms for entering responses about "59 "each image - and so just allow flipping through the images.")60#p.add_argument("-c", "--config-file", help="Path of yaml config file", env_var="FLIPBOOK_CONFIG_FILE")61p.add_argument("-v", "--verbose", action='count', default=0, help="Print more info")62p.add_argument("--host", default="127.0.0.1", env_var="HOST", help="Listen for connections on this hostname or IP")63p.add_argument("-p", "--port", default="8080", env_var="PORT", type=int, help="Listen for connections on this port")64p.add_argument("--dev-mode", action="store_true", env_var="DEV", help="Run server in developer mode so it reloads "65 "html templates and source code if they're changed")66p.add_argument("directory", default=".", nargs="?", help="Top-level directory to search for images and data files")67args = p.parse_args()68if args.verbose > 1:69 p.print_values()70if not os.path.isdir(args.directory):71 p.error(f"{args.directory} directory not found")72args.directory = os.path.realpath(args.directory)73def parse_table(path):74 if not os.path.isfile(path):75 raise ValueError(f"{path} not found")76 try:77 if is_excel_table(path):78 df = pd.read_excel(path, engine="openpyxl")79 else:80 df = pd.read_table(path)81 except Exception as e:82 raise ValueError(f"Unable to parse {path}: {e}")83 # validate table contents84 if PATH_COLUMN not in df.columns:85 raise ValueError(f"{path} must have a column named '{PATH_COLUMN}'")86 df.set_index(PATH_COLUMN, inplace=True, drop=False)87 df = df.fillna('')88 print(f"Parsed {len(df)} rows from {path}")89 return df90# search directory for images and data files91RELATIVE_DIRECTORY_TO_DATA_FILES_LIST = get_relative_directory_to_data_files_list(92 args.directory,93 args.include,94 args.exclude,95 verbose=args.verbose)96if not RELATIVE_DIRECTORY_TO_DATA_FILES_LIST:97 p.error(f"No images or data files found in {args.directory}")98# parse metadata from flipbook_metadata.json files99METADATA_COLUMNS, RELATIVE_DIRECTORY_TO_METADATA = get_relative_directory_to_metadata(100 args.directory,101 RELATIVE_DIRECTORY_TO_DATA_FILES_LIST,102 verbose=args.verbose)103# parse metadata from the metadata_table if specified104if args.metadata_table and os.path.isfile(args.metadata_table):105 #args.metadata_table = os.path.join(args.directory, args.metadata_table)106 try:107 df = parse_table(args.metadata_table)108 except ValueError as e:109 p.error(str(e))110 for relative_directory, row in df.iterrows():111 metadata_dict = {k: v for k, v in row.to_dict().items() if k != PATH_COLUMN}112 if relative_directory not in RELATIVE_DIRECTORY_TO_METADATA:113 RELATIVE_DIRECTORY_TO_METADATA[relative_directory] = {}114 if args.verbose:115 print(f"Setting new metadata row for {relative_directory} to {metadata_dict}")116 else:117 if args.verbose:118 print(f"Updating existing metadata row for {relative_directory} to {metadata_dict}")119 RELATIVE_DIRECTORY_TO_METADATA[relative_directory].update(metadata_dict)120 for key in metadata_dict:121 if key not in METADATA_COLUMNS:122 METADATA_COLUMNS.append(key)123# define input form fields to show on each data page124FORM_SCHEMA = [125 {126 "type": "radio",127 "columnName": "Verdict",128 "choices": [129 {"value": "normal", "label": "Normal"},130 {"value": "intermediate", "label": "Intermediate"},131 {"value": "full-expansion", "label": "Full Expansion"},132 {"value": "double-expansion", "label": "Double Expansion"},133 ]134 },135 {136 "type": "radio",137 "columnName": "Confidence",138 "inputLabel": "Confidence",139 "choices": [140 {"value": "borderline", "label": "Borderline"},141 {"value": "confident", "label": "Confident"},142 ]143 },144 {145 "type": "text",146 "columnName": "Notes",147 "size": 60148 }149]150if args.generate_static_website:151 FORM_SCHEMA = {}152if args.form_schema_json:153 print(f"Loading form schema from {args.form_schema_json}")154 try:155 if os.path.isfile(args.form_schema_json):156 with open(args.form_schema_json, "rt") as f:157 FORM_SCHEMA = json.load(f)158 elif args.form_schema_json.startswith("http"):159 # Convert https://github.com/broadinstitute/flipbook/blob/main/flipbook/__init__.py to the raw url:160 # https://raw.githubusercontent.com/broadinstitute/flipbook/main/flipbook/__init__.py161 github_match = re.search("//github.com/(.*)/blob/(.*)", args.form_schema_json)162 if github_match:163 part1 = github_match.group(1)164 part2 = github_match.group(2)165 github_raw_file_url = f"https://raw.githubusercontent.com/{part1}/{part2}"166 if args.verbose:167 print(f"Converting form schema url {args.form_schema_json} to {github_raw_file_url}")168 args.form_schema_json = github_raw_file_url169 r = requests.get(url=args.form_schema_json)170 FORM_SCHEMA = r.json()171 except Exception as e:172 p.error(f"Couldn't parse {args.form_schema_json}: {e}")173FORM_SCHEMA_COLUMNS = [r['columnName'] for r in FORM_SCHEMA]174# validate FORM_SCHEMA and determine keyboard shortcuts175FORM_RADIO_BUTTON_KEYBOARD_SHORTCUTS = {}176for i, form_schema_row in enumerate(FORM_SCHEMA):177 if not isinstance(form_schema_row, dict):178 raise ValueError(f"FORM_SCHEMA row {i} must be a dictionary")179 missing_keys = {'type', 'columnName'} - set(form_schema_row.keys())180 if missing_keys:181 raise ValueError(f"FORM_SCHEMA row {i} is missing values for these keys: {', '.join(missing_keys)}")182 if form_schema_row['type'] not in ('text', 'radio'):183 raise ValueError(f"FORM_SCHEMA row {i} has unexpected 'type' value: {form_schema_row['type']}")184 if 'name' not in form_schema_row:185 form_schema_row['name'] = form_schema_row['columnName'].lower()186 form_schema_row['name'] = re.sub("[^a-zA-Z0-9_]", "_", form_schema_row['name'])187 if 'inputLabel' not in form_schema_row:188 form_schema_row['inputLabel'] = form_schema_row['columnName']189 if form_schema_row['type'] == 'radio':190 if 'choices' not in form_schema_row or not isinstance(form_schema_row['choices'], list):191 raise ValueError(f"FORM_SCHEMA row {i} is missing a 'choices' list")192 for choice in form_schema_row['choices']:193 if not isinstance(choice, dict):194 raise ValueError(f"FORM_SCHEMA row {i} must 'choices' list must contain dictionaries")195 missing_keys = {'value', 'label'} - set(choice.keys())196 if missing_keys:197 raise ValueError(f"FORM_SCHEMA row {i} 'choices' list has entries where these keys are missing: {', '.join(missing_keys)}")198 label_without_html = re.sub("<[^<]+?>", "", choice['label']).strip()199 first_letter = (label_without_html or choice["value"])[0]200 FORM_RADIO_BUTTON_KEYBOARD_SHORTCUTS[first_letter] = choice['value']201 print(f"Form Keyboard Shortcut: {first_letter} => {choice['label']}")202# parse or create FORM_RESPONSES dict for storing user responses203FORM_RESPONSES = {}204# if a form_responses_table is provided with additional columns which are not in the current FORM_SCHEMA (eg. if the205# form schema changes, save this info here so it's not lost when the table is updated after new form responses.206EXTRA_COLUMNS_IN_FORM_RESPONSES_TABLE = []207EXTRA_DATA_IN_FORM_RESPONSES_TABLE = {}208if FORM_SCHEMA:209 args.form_responses_table = os.path.join(args.directory, args.form_responses_table)210 args.form_responses_table_is_excel = is_excel_table(args.form_responses_table)211 if os.path.isfile(args.form_responses_table):212 try:213 df = parse_table(args.form_responses_table)214 except ValueError as e:215 p.error(str(e))216 EXTRA_COLUMNS_IN_FORM_RESPONSES_TABLE = [c for c in df.columns if c not in FORM_SCHEMA_COLUMNS and c not in METADATA_COLUMNS and c != PATH_COLUMN]217 else:218 # make sure the table can be written out later219 if not os.access(os.path.dirname(args.form_responses_table), os.W_OK):220 p.error(f"Unable to create {args.form_responses_table}")221 # create empty table in memory222 df = pd.DataFrame(columns=[PATH_COLUMN] + FORM_SCHEMA_COLUMNS)223 df.set_index(PATH_COLUMN, inplace=True, drop=False)224 # store form responses in memory.225 # NOTE: This is not thread-safe and assumes a single-threaded server. For multi-threaded226 # or multi-process servers like gunicorn, this will need to be replaced with a sqlite or redis backend.227 FORM_RESPONSES = collections.OrderedDict()228 for relative_directory, row in df.iterrows():229 row_as_dict = row.to_dict()230 FORM_RESPONSES[relative_directory] = {k: v for k, v in row_as_dict.items() if k in FORM_SCHEMA_COLUMNS}231 EXTRA_DATA_IN_FORM_RESPONSES_TABLE[relative_directory] = {k: v for k, v in row_as_dict.items() if k not in FORM_SCHEMA_COLUMNS and k != PATH_COLUMN}232 print(f"Will save form responses to {args.form_responses_table} (columns: {', '.join(FORM_SCHEMA_COLUMNS + EXTRA_COLUMNS_IN_FORM_RESPONSES_TABLE)})")233if args.sort_by:234 valid_columns = set([PATH_COLUMN])235 valid_columns.update(FORM_SCHEMA_COLUMNS)236 if METADATA_COLUMNS:237 valid_columns.update(METADATA_COLUMNS)238 invalid_values = ", ".join([f"'{s}'" for s in args.sort_by if s not in valid_columns])239 if invalid_values:240 p.error(f"{invalid_values} column(s) not found in metadata. --sort-by value should be one of: " +241 ", ".join(valid_columns))242 print(f"Sorting {len(RELATIVE_DIRECTORY_TO_DATA_FILES_LIST)} pages by {', '.join(args.sort_by)}")243 def get_sort_key(i):244 relative_dir = i[0]245 sort_key = []246 for s in args.sort_by:247 if s == PATH_COLUMN:248 sort_key.append(relative_dir)249 continue250 form_value = FORM_RESPONSES.get(relative_dir, {}).get(s)251 if form_value is not None:252 sort_key.append(str(form_value))253 continue254 metadata_value = RELATIVE_DIRECTORY_TO_METADATA.get(relative_dir, {}).get(s)255 if metadata_value is not None:256 sort_key.append(str(metadata_value))257 continue258 return tuple(sort_key)259 RELATIVE_DIRECTORY_TO_DATA_FILES_LIST = sorted(RELATIVE_DIRECTORY_TO_DATA_FILES_LIST, key=get_sort_key, reverse=args.reverse_sort)260def send_file(path):261 print(f"Sending {args.directory} {path}")262 if path.startswith("favicon"):263 return Response(pkg_resources.resource_stream('flipbook', 'icons/favicon.png'), mimetype='image/png')264 return send_from_directory(args.directory, path, as_attachment=True)265def get_static_data_page_url(page_number, last):266 i = page_number - 1267 if i < 0 or i >= len(RELATIVE_DIRECTORY_TO_DATA_FILES_LIST):268 raise ValueError(f"page_number arg is out of bounds. It must be between 1 and {len(RELATIVE_DIRECTORY_TO_DATA_FILES_LIST)}")269 relative_dir, _ = RELATIVE_DIRECTORY_TO_DATA_FILES_LIST[i]270 name = relative_dir.replace("/", "__")271 return f"page_{name}.html"272def main():273 # add a ctime(..) function to allow the last-changed-time of a path to be computed within a jinja template274 jinja2.environment.DEFAULT_FILTERS['ctime'] = lambda path: int(os.path.getctime(path)) if os.path.isfile(path) else 0275 from flipbook.main_list import main_list_handler276 from flipbook.data_page import data_page_handler277 from flipbook.save import save_form_handler278 app = Flask(__name__)279 if args.generate_static_website:280 os.makedirs(WEBSITE_DIR, exist_ok=True)281 with open(os.path.join(WEBSITE_DIR, "index.html"), "wt") as f:282 f.write(main_list_handler(is_static_website=True).get_data(as_text=True))283 with open(os.path.join(WEBSITE_DIR, "favicon.png"), "wb") as f:284 f.write(pkgutil.get_data('flipbook', 'icons/favicon.png'))285 last_page_number = len(RELATIVE_DIRECTORY_TO_DATA_FILES_LIST)286 for i, (relative_directory, data_file_types_and_paths) in enumerate(RELATIVE_DIRECTORY_TO_DATA_FILES_LIST):287 page_number = i + 1288 with app.test_request_context(get_data_page_url(page_number, last_page_number)):289 page_dir = os.path.join(WEBSITE_DIR, relative_directory)290 os.makedirs(page_dir, exist_ok=True)291 for data_file_type, data_file in data_file_types_and_paths:292 if data_file_type in (METADATA_JSON_FILE_TYPE, CONTENT_HTML_FILE_TYPE):293 continue294 print("Copying", data_file_type, data_file, "to", page_dir)295 shutil.copy(data_file, page_dir)296 with open(os.path.join(WEBSITE_DIR, get_static_data_page_url(page_number, last_page_number)), "wt") as f:297 f.write(data_page_handler(is_static_website=True).get_data(as_text=True))298 print("Done")299 print(f"Generated static website in the ./{WEBSITE_DIR} directory")300 sys.exit(0)301 # start web server302 CORS(app)303 app.url_map.strict_slashes = False304 app.add_url_rule('/', view_func=main_list_handler, methods=['GET'])305 app.add_url_rule('/page', view_func=data_page_handler, methods=['POST', 'GET'])306 app.add_url_rule('/save', view_func=save_form_handler, methods=['POST'])307 app.add_url_rule('/<path:path>', view_func=send_file, methods=['GET'])308 os.environ["WERKZEUG_RUN_MAIN"] = "true"309 host = os.environ.get('HOST', args.host)310 port = int(os.environ.get('PORT', args.port))311 if args.verbose:312 print(f"Connecting to {host}:{port}")313 try:314 app.run(315 debug=args.dev_mode,316 host=host,317 port=port)318 except OSError as e:319 if "already in use" in str(e):320 p.error(f"Port {port} is already in use by another process. Use -p to specify a different port.")321 else:...
data_page.py
Source:data_page.py
1import os2from flask import request, Response3from pprint import pprint, pformat4from flipbook import args, RELATIVE_DIRECTORY_TO_DATA_FILES_LIST, FORM_SCHEMA, FORM_RESPONSES, \5 RELATIVE_DIRECTORY_TO_METADATA, FORM_RADIO_BUTTON_KEYBOARD_SHORTCUTS, EXTRA_DATA_IN_FORM_RESPONSES_TABLE, \6 get_static_data_page_url, DATA_PAGE_HEADER_FILENAME7from flipbook.utils import load_jinja_template, get_data_page_url, CONTENT_HTML_FILE_TYPE, \8 IMAGE_FILE_TYPE9DATA_PAGE_TEMPLATE = None10def data_page_handler(is_static_website=False):11 global DATA_PAGE_TEMPLATE12 if DATA_PAGE_TEMPLATE is None or args.dev_mode:13 DATA_PAGE_TEMPLATE = load_jinja_template("data_page")14 if args.verbose:15 print(f"data_page_handler received {request.url}")16 params = {}17 if dict(request.args):18 params.update(dict(request.args))19 if dict(request.values):20 params.update(dict(request.values))21 json_args = request.get_json(force=True, silent=True)22 if 'i' not in params:23 params.update(json_args or {})24 if args.verbose > 1:25 print(f"data_page_handler request.data: {pformat(request.data)}")26 print(f"data_page_handler request.form: {bool(request.form)} {pformat(request.form)}")27 print(f"data_page_handler request.args: {bool(request.args)} {pformat(request.args)}")28 print(f"data_page_handler request.values: {bool(request.values)} {pformat(request.values)}")29 print(f"data_page_handler request.get_json(..): {bool(json_args)} {pformat(json_args)}")30 print(f"data_page_handler request.__dict__: {pformat(request.__dict__)}")31 print(f"data_page_handler params: {params}")32 i = None33 relative_dir = params.get("path")34 if relative_dir:35 # override i36 for idx, (known_relative_dir, _) in enumerate(RELATIVE_DIRECTORY_TO_DATA_FILES_LIST):37 if relative_dir == known_relative_dir:38 i = idx + 139 break40 else:41 print(f"ERROR: path param '{relative_dir}' not recognized. Falling back on using i param.")42 if i is None:43 i = params.get("i")44 try:45 if isinstance(i, list):46 i = int(i[0])47 else:48 i = int(i)49 except (ValueError, TypeError) as e:50 print(f"ERROR: unable to parse parameter i: '{i}': {type(e).__name__} {e}. Setting i = 1.")51 i = 152 last = params.get("last", i)53 try:54 if isinstance(i, list):55 last = int(last[0])56 else:57 last = int(last)58 except (ValueError, TypeError) as e:59 print(f"ERROR: unable to parse parameter 'last': '{last}': {type(e).__name__} {e}. Setting last = {i}.")60 last = i61 if last < 1:62 print(f"ERROR: parameter 'last' (= {last}) is less than 1. Resetting it to 1.")63 last = 164 if i < 1:65 print(f"ERROR: parameter i (= {i}) is less than 1. Resetting it to 1.")66 i = 167 if i > len(RELATIVE_DIRECTORY_TO_DATA_FILES_LIST):68 print(f"ERROR: parameter i (= {i}) is greater than the # of pages "69 f"(= {len(RELATIVE_DIRECTORY_TO_DATA_FILES_LIST)}). "70 f"Resetting it to {len(RELATIVE_DIRECTORY_TO_DATA_FILES_LIST)}.")71 i = len(RELATIVE_DIRECTORY_TO_DATA_FILES_LIST)72 relative_dir, data_file_types_and_paths = RELATIVE_DIRECTORY_TO_DATA_FILES_LIST[i - 1]73 image_file_paths = []74 for data_file_type, data_file_path in data_file_types_and_paths:75 if data_file_type == IMAGE_FILE_TYPE:76 image_file_paths.append(data_file_path)77 metadata_json_dict = RELATIVE_DIRECTORY_TO_METADATA.get(relative_dir, {})78 metadata_json_dict.update(EXTRA_DATA_IN_FORM_RESPONSES_TABLE.get(relative_dir, {}))79 content_html_strings = []80 for data_file_type, data_file_path in data_file_types_and_paths:81 if data_file_type != CONTENT_HTML_FILE_TYPE:82 continue83 with open(os.path.join(args.directory, data_file_path), "rt") as f:84 content_string = f.read()85 content_html_strings.append((data_file_path, content_string))86 if args.verbose:87 print(f"data_page_handler returning i={i}, last={last}, relative_directory={relative_dir}, "88 f"{len(image_file_paths)} image_file_paths, {len(metadata_json_dict)} records in metadata_json_dict, "89 f"{len(content_html_strings)} content_html_strings")90 data_page_header_html = ""91 if os.path.isfile(os.path.join(args.directory, DATA_PAGE_HEADER_FILENAME)):92 with open(os.path.join(args.directory, DATA_PAGE_HEADER_FILENAME), "rt") as f:93 data_page_header_html = f.read()94 html = DATA_PAGE_TEMPLATE.render(95 i=i,96 last=last,97 header_html=data_page_header_html,98 relative_directory=relative_dir,99 image_file_paths=image_file_paths,100 metadata_json_dict=metadata_json_dict,101 content_html_strings=content_html_strings,102 get_data_page_url=get_data_page_url if not is_static_website else get_static_data_page_url,103 form_schema=FORM_SCHEMA,104 form_radio_button_keyboard_shortcuts=FORM_RADIO_BUTTON_KEYBOARD_SHORTCUTS,105 form_responses=FORM_RESPONSES.get(relative_dir, {}),106 is_static_website=is_static_website,107 )...
main_list.py
Source:main_list.py
1import os2from flask import request, Response3from flipbook import args, RELATIVE_DIRECTORY_TO_DATA_FILES_LIST, FORM_RESPONSES, FORM_SCHEMA_COLUMNS, \4 RELATIVE_DIRECTORY_TO_METADATA, METADATA_COLUMNS, EXTRA_DATA_IN_FORM_RESPONSES_TABLE, \5 EXTRA_COLUMNS_IN_FORM_RESPONSES_TABLE, get_static_data_page_url, MAIN_PAGE_HEADER_FILENAME6from flipbook.utils import load_jinja_template, get_data_page_url7MAIN_LIST_TEMPLATE = None8def main_list_handler(is_static_website=False):9 global MAIN_LIST_TEMPLATE10 if MAIN_LIST_TEMPLATE is None or args.dev_mode:11 MAIN_LIST_TEMPLATE = load_jinja_template("main_list")12 if args.verbose:13 print(f"main_list_handler received {request.url}")14 data_files_list = [15 (page_number + 1, relative_directory, data_file_types_and_paths)16 for page_number, (relative_directory, data_file_types_and_paths) in enumerate(RELATIVE_DIRECTORY_TO_DATA_FILES_LIST)17 ]18 # combine metadata from 2 potential sources: RELATIVE_DIRECTORY_TO_METADATA and EXTRA_DATA_IN_FORM_RESPONSES_TABLE19 metadata_columns = []20 metadata_dict = {}21 if not args.hide_metadata_on_home_page:22 metadata_columns = METADATA_COLUMNS23 if not is_static_website:24 metadata_columns += EXTRA_COLUMNS_IN_FORM_RESPONSES_TABLE25 for relative_dir in list(RELATIVE_DIRECTORY_TO_METADATA.keys()) + list(EXTRA_DATA_IN_FORM_RESPONSES_TABLE.keys()):26 metadata_dict[relative_dir] = dict(RELATIVE_DIRECTORY_TO_METADATA.get(relative_dir, {}))27 metadata_dict[relative_dir].update(dict(EXTRA_DATA_IN_FORM_RESPONSES_TABLE.get(relative_dir, {})))28 main_page_header_html = ""29 if os.path.isfile(os.path.join(args.directory, MAIN_PAGE_HEADER_FILENAME)):30 with open(os.path.join(args.directory, MAIN_PAGE_HEADER_FILENAME), "rt") as f:31 main_page_header_html = f.read()32 html = MAIN_LIST_TEMPLATE.render(33 header_html=main_page_header_html,34 data_files_list=data_files_list,35 get_data_page_url=get_data_page_url if not is_static_website else get_static_data_page_url,36 form_column_names=FORM_SCHEMA_COLUMNS,37 form_responses_dict=FORM_RESPONSES,38 metadata_column_names=metadata_columns,39 metadata_dict=metadata_dict,40 form_responses_table_path=args.form_responses_table,41 is_static_website=is_static_website,42 )...
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!