From 2246e74ec8202e62ba3fcde4666f6df3f1f3ca53 Mon Sep 17 00:00:00 2001 From: Quinten Steenhuis Date: Fri, 17 May 2024 18:45:25 -0400 Subject: [PATCH] Fix #64, also fix types and formatting --- docassemble/ALDashboard/aldashboard.py | 44 +++++++++++++------ .../data/questions/install_assembly_line.yml | 19 +++++++- .../data/questions/install_packages.yml | 39 +++++++++++----- docassemble/ALDashboard/package_scanner.py | 6 +-- docassemble/ALDashboard/requirements.txt | 3 +- docassemble/ALDashboard/translation.py | 8 +++- pyproject.toml | 1 - 7 files changed, 87 insertions(+), 33 deletions(-) diff --git a/docassemble/ALDashboard/aldashboard.py b/docassemble/ALDashboard/aldashboard.py index b2d47b3..8b6ce94 100644 --- a/docassemble/ALDashboard/aldashboard.py +++ b/docassemble/ALDashboard/aldashboard.py @@ -67,8 +67,11 @@ ] -def install_from_github_url(url: str, branch: str = ""): +def install_from_github_url(url: str, branch: str = "", pat: Optional[str] = None): giturl = url.strip().rstrip("/") + if pat: + # modify so it looks like https://e8cc02bec7061de98ba4851263638d7483f63d41:x-oauth-basic@github.com/johnsmith/docassemble-missouri-familylaw + giturl = re.sub(r"^https://", f"https://{pat}:x-oauth-basic@", giturl) if isinstance(branch, str): branch = branch.strip() if not branch: @@ -221,8 +224,10 @@ def speedy_get_sessions( """ ) if not filename: - if not user_has_privilege(['admin', 'developer']): - raise Exception("You must provide a filename to filter sessions unless you are a developer or administrator.") + if not user_has_privilege(["admin", "developer"]): + raise Exception( + "You must provide a filename to filter sessions unless you are a developer or administrator." + ) filename = None # Explicitly treat empty string as equivalent to None if not user_id: user_id = None @@ -233,7 +238,12 @@ def speedy_get_sessions( with db.connect() as con: rs = con.execute( get_sessions_query, - {"user_id": user_id, "filename": filename, "filter_step1": filter_step1, "metadata": metadata_key_name}, + { + "user_id": user_id, + "filename": filename, + "filter_step1": filter_step1, + "metadata": metadata_key_name, + }, ) sessions = [session for session in rs] @@ -374,7 +384,8 @@ def nicer_interview_filename(filename: str) -> str: return filename_parts[0] -def list_question_files_in_package(package_name:str) -> List[str]: + +def list_question_files_in_package(package_name: str) -> Optional[List[str]]: """ List all the files in the 'data/questions' directory of a package. @@ -386,19 +397,22 @@ def list_question_files_in_package(package_name:str) -> List[str]: """ try: # Locate the directory within the package - directory_path = pkg_resources.resource_filename(package_name, 'data/questions') + directory_path = pkg_resources.resource_filename(package_name, "data/questions") # List all files in the directory if os.path.isdir(directory_path): files = os.listdir(directory_path) # Filter out directories, only keep files - files = [f for f in files if os.path.isfile(os.path.join(directory_path, f))] + files = [ + f for f in files if os.path.isfile(os.path.join(directory_path, f)) + ] return files else: return [] except Exception as e: log(f"An error occurred with package '{package_name}': {e}") - return None + return [] + def list_question_files_in_docassemble_packages(): """ @@ -407,18 +421,22 @@ def list_question_files_in_docassemble_packages(): Returns: Dict[str, List[str]]: A dictionary where the keys are package names and the values are lists of filenames in the 'data/questions' directory of the package. """ - packages = get_package_info()[0] # get_package_info returns a tuple, the packages are in index 0 + packages = get_package_info()[ + 0 + ] # get_package_info returns a tuple, the packages are in index 0 - filtered_packages = [pkg for pkg in packages if pkg.package.name.startswith('docassemble.')] + filtered_packages = [ + pkg for pkg in packages if pkg.package.name.startswith("docassemble.") + ] result = {} # Iterate over each filtered package and list files in 'data/questions' for package in filtered_packages: package_name = package.package.name - + files = list_question_files_in_package(package_name) if files: - result[package_name] = files + result[package_name] = files - return result \ No newline at end of file + return result diff --git a/docassemble/ALDashboard/data/questions/install_assembly_line.yml b/docassemble/ALDashboard/data/questions/install_assembly_line.yml index 3024d2c..5f5c1d4 100644 --- a/docassemble/ALDashboard/data/questions/install_assembly_line.yml +++ b/docassemble/ALDashboard/data/questions/install_assembly_line.yml @@ -206,7 +206,7 @@ fields: - Server short title: the_config['default short title'] default: ${ the_config.get('default short title') } - App name: the_config['appname'] - default: ${ the_config.get('appname', '') } + default: ${ the_config.get('appname', the_config.get('default short title')) } - note: | --- Some features require background processing, which means you need @@ -371,6 +371,9 @@ subquestion: | 1. Let users give feedback about an interview. 2. Display a "last updated" date on the "About" page of an interview. + 1. Install and update packages from GitHub. + + We recommend using **2 separate GitHub accounts** for these tasks. First, in order to use GitHub you should create a dedicated GitHub account. You can use an account that already exists, but we recommend having an account that only handles Assembly Line functionality. @@ -383,9 +386,21 @@ subquestion: | 1. Tap the checkbox for "repo" permissions. 1. Finish creating the personal access token. 1. Copy the token and paste it below + + If you have a private repository, you will need a second token with "repo" permissions. + + This token is only used to install and update packages, and should not be the same + as the account used for feedback. It is OK for this token to be associated with your + personal GitHub account. fields: - - Personal access token: the_config['github issues']['token'] + - Personal access token for authoring issues: the_config['github issues']['token'] default: ${ the_config['github issues'].get('token', '') } + - Personal access token for installing private repositories: the_config['assembly line']['github install token'] + default: ${ the_config['assembly line'].get('github install token', '') } + required: False + help: | + If you have private repositories, you will need a second token with "repo" permissions. + This token is only used to install and update packages. --- id: github owners list continue button field: github_owners_list diff --git a/docassemble/ALDashboard/data/questions/install_packages.yml b/docassemble/ALDashboard/data/questions/install_packages.yml index fd5e7cc..53c2f24 100644 --- a/docassemble/ALDashboard/data/questions/install_packages.yml +++ b/docassemble/ALDashboard/data/questions/install_packages.yml @@ -26,10 +26,20 @@ question: | list collect: True fields: - Github URL: packages[i].github_url + - This is a private GitHub repo: packages[i].is_private + datatype: yesno + - Github [Personal Access Token](https://docassemble.org/docs/packages.html#github_install): packages[i].github_token + show if: packages[i].is_private + default: | + ${ get_config('assembly line',{}).get("github install token", "") } + - Also add an alias: packages[i].add_alias + datatype: yesno - YAML filename: packages[i].yaml_name + show if: packages[i].add_alias - Short name or alias (no spaces): packages[i].alias validate: | lambda y: y.isidentifier() + show if: packages[i].add_alias --- code: | install_packages_task = background_action('install_packages_event') @@ -38,19 +48,21 @@ event: install_packages_event code: | background_error_action("bg_failure") for package in packages: - pkgname = install_from_github_url(package.github_url) + pkgname = install_from_github_url(package.github_url, pat=package.github_token if package.is_private else None) reset(pkgname) - the_config = da_get_config() - if not the_config.get("dispatch"): - the_config["dispatch"] = {} - for package in packages: - package_regex = r"https:\/\/github\.com\/.*\/docassemble-([\w]*)" - match = re.search(package_regex, package.github_url) - if match: - package_path = f"docassemble.{ match.groups()[0] }:data/questions/{ package.yaml_name }" - the_config["dispatch"][package.alias] = package_path - results = da_write_config(the_config) + if any((package.add_alias for package in packages)): + the_config = da_get_config() + if not the_config.get("dispatch"): + the_config["dispatch"] = {} + for package in packages: + if package.add_alias: + package_regex = r"https:\/\/github\.com\/.*\/docassemble-([\w]*)" + match = re.search(package_regex, package.github_url) + if match: + package_path = f"docassemble.{ match.groups()[0] }:data/questions/{ package.yaml_name }" + the_config["dispatch"][package.alias] = package_path + results = da_write_config(the_config) background_response_action("bg_success") --- @@ -76,13 +88,18 @@ subquestion: | % if it_worked: It may take a few minutes for the installation process to complete. + % if any((package.add_alias for package in packages)): You can now use these links to reach your interviews: % for package in packages: + % if package.add_alias: * [${package.alias}](/start/${package.alias}) + % endif % endfor + % endif % else: Something went wrong. Check the [worker.log](/logs?file=worker.log) to learn what. % endif + buttons: - Restart: restart \ No newline at end of file diff --git a/docassemble/ALDashboard/package_scanner.py b/docassemble/ALDashboard/package_scanner.py index 28cfe35..3258331 100644 --- a/docassemble/ALDashboard/package_scanner.py +++ b/docassemble/ALDashboard/package_scanner.py @@ -163,9 +163,9 @@ def fetch_github_repo_version(repo_list, key_pkgs, github_user) -> dict: version_num = decoded_line[str_start:str_end][9:].replace( "',\n", "" ) - v[ - "version" - ] = version_num # Add version number to the original repo_list. + v["version"] = ( + version_num # Add version number to the original repo_list. + ) has_version_num = True break diff --git a/docassemble/ALDashboard/requirements.txt b/docassemble/ALDashboard/requirements.txt index a7f2c55..615a350 100644 --- a/docassemble/ALDashboard/requirements.txt +++ b/docassemble/ALDashboard/requirements.txt @@ -3,6 +3,7 @@ docassemble.webapp types-setuptools types-pycurl types-requests +types-setuptools mypy openai -tiktoken \ No newline at end of file +tiktoken diff --git a/docassemble/ALDashboard/translation.py b/docassemble/ALDashboard/translation.py index 771fff5..fd69ed4 100644 --- a/docassemble/ALDashboard/translation.py +++ b/docassemble/ALDashboard/translation.py @@ -54,7 +54,9 @@ class Translation(NamedTuple): file: DAFile # an XLSX or XLIFF file - untranslated_words: int # Word count for all untranslated segments that are not Mako or HTML + untranslated_words: ( + int # Word count for all untranslated segments that are not Mako or HTML + ) untranslated_segments: int # Number of rows in the output that have untranslated text - one for each question, subquestion, field, etc. total_rows: int @@ -69,7 +71,9 @@ def translation_file(yaml_filename: str, tr_lang: str) -> Translation: This code was adjusted from the Flask endpoint-only version in server.py. XLIFF support was removed for now but can be added later. """ - filetype: str = "XLSX" # Look in server.py for support of XLIFF format, but we won't implement it here + filetype: str = ( + "XLSX" # Look in server.py for support of XLIFF format, but we won't implement it here + ) output_file = DAFile() setup_translation() if yaml_filename is None or not re.search(r"\S", yaml_filename): diff --git a/pyproject.toml b/pyproject.toml index a0d883e..de17cf1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,4 +63,3 @@ ignore_missing_imports = true [[tool.mypy.overrides]] module="mako.*" ignore_missing_imports = true -