From e4350ef121c7c0d4ccf9eac0eda892a1c3a02ac8 Mon Sep 17 00:00:00 2001 From: Federico Negri Date: Mon, 29 Aug 2022 17:52:49 +0200 Subject: [PATCH 1/5] Update quickstart (add get_project_by_name) --- ansys/rep/client/jms/api/jms_api.py | 43 +++-- doc/source/quickstart.rst | 159 +++++++++++------- .../mapdl_motorbike_frame/project_setup.py | 2 +- 3 files changed, 132 insertions(+), 72 deletions(-) diff --git a/ansys/rep/client/jms/api/jms_api.py b/ansys/rep/client/jms/api/jms_api.py index d0e6b2a46..d394d8c11 100644 --- a/ansys/rep/client/jms/api/jms_api.py +++ b/ansys/rep/client/jms/api/jms_api.py @@ -2,7 +2,7 @@ import logging import os import time -from typing import List +from typing import List, Union import uuid from ansys.rep.client.exceptions import REPError @@ -53,9 +53,18 @@ def get_projects(self, **query_params): """Return a list of projects, optionally filtered by given query parameters""" return get_projects(self.client, self.url, **query_params) - def get_project(self, id=None, name=None): + def get_project(self, id): """Return a single project for given project id""" - return get_project(self.client, self.url, id, name) + return get_project(self.client, self.url, id) + + def get_project_by_name(self, name, last_created=True) -> Union[Project, List[Project]]: + """ + Query projects by name. If no projects are found, an empty list is returned. + In case of multiple projects with same name: + - If `last_created = True`, the last created project is returned + - If `last_created = False`, the full list of projects with given name is returned + """ + return get_project_by_name(self.client, self.url, name, last_created) def create_project(self, project, replace=False, as_objects=True): """Create a new project""" @@ -156,18 +165,13 @@ def get_projects(client, api_url, as_objects=True, **query_params) -> List[Proje return schema.load(data) -def get_project(client, api_url, id=None, name=None) -> Project: +def get_project(client, api_url, id) -> Project: """ Return a single project """ - params = {} - if name: - url = f"{api_url}/projects/" - params["name"] = name - else: - url = f"{api_url}/projects/{id}" - r = client.session.get(url, params=params) + url = f"{api_url}/projects/{id}" + r = client.session.get(url) if len(r.json()["projects"]): schema = ProjectSchema() @@ -175,6 +179,23 @@ def get_project(client, api_url, id=None, name=None) -> Project: return None +def get_project_by_name(client, api_url, name, last_created=True) -> Union[Project, List[Project]]: + """ + Return a single project + """ + + params = {"name": name} + if last_created: + params["sort"] = "-creation_time" + params["limit"] = 1 + + projects = get_projects(client, api_url, **params) + + if len(projects) == 1: + return projects[0] + return projects + + def create_project(client, api_url, project, replace=False, as_objects=True) -> Project: url = f"{api_url}/projects/" diff --git a/doc/source/quickstart.rst b/doc/source/quickstart.rst index bbc56b969..f00da988a 100644 --- a/doc/source/quickstart.rst +++ b/doc/source/quickstart.rst @@ -1,7 +1,7 @@ .. _quickstart: Quickstart -=============== +========== This guide will walk you through the basics of interacting with a REP server. More elaborated examples are available in the :ref:`Examples ` chapter, while detailed documentation can be found in the :ref:`Code Documentation `. @@ -20,7 +20,7 @@ Let's start by connecting to a REP server running on the localhost with default .. code-block:: python from ansys.rep.client import Client - from ansys.rep.client.jms import JmsApi + from ansys.rep.client.jms import JmsApi, ProjectApi client = Client(rep_url="https://localhost:8443/rep", username="repadmin", password="repadmin") @@ -41,7 +41,7 @@ Query projects statistics to find out how many design points are currently runni Create a demo project: the MAPDL motorbike frame example --------------------------------------------------------- -Create a project consisting of an Ansys APDL beam model +Create a project consisting of an Ansys Mechanical APDL beam model of a tubular steel trellis motorbike-frame. .. only:: builder_html @@ -68,27 +68,43 @@ Most ``get`` functions support filtering by query parameters. .. code-block:: python - project = jms_api.get_project(id="mapdl_motorbike_frame") + project = jms_api.get_project_by_name(name="Mapdl Motorbike Frame") + project_api = ProjectApi(client, project.id) # Get all design points with all fields - jobs = project.get_jobs() + jobs = project_api.get_jobs() # Get id and parameter values for all evaluated design points - jobs = project.get_jobs(fields=["id", "values"], eval_status="evaluated") + jobs = project_api.get_jobs(fields=["id", "values"], eval_status="evaluated") # Get name and elapsed time of max 5 evaluated design points - jobs = project.get_jobs(fields=["name", "elapsed_time"], + jobs = project_api.get_jobs(fields=["name", "elapsed_time"], eval_status="evaluated", limit=5) + for job in jobs: + print(job) + # { + # "id": "02qoqedl8QCjkuLcqCi10Q", + # "name": "Job.0", + # "priority": 0, + # "elapsed_time": 35.275044 + # } + # { + # "id": "02qoqedlDMO1LrSGoHQqnT", + # "name": "Job.1", + # "priority": 0, + # "elapsed_time": 34.840801 + # } + # ... # Get all design points sorted by fitness value in ascending order - jobs = project.get_jobs(sort="fitness") + jobs = project_api.get_jobs(sort="fitness") # Get all design points sorted by fitness value in descending order - jobs = project.get_jobs(sort="-fitness") + jobs = project_api.get_jobs(sort="-fitness") # Get all design points sorted by the parameters tube1 and weight - jobs = project.get_jobs(sort=["values.tube1", "values.weight"]) - print([(dp.values["tube1"], dp.values["weight"]) for dp in jobs]) + jobs = project_api.get_jobs(sort=["values.tube1", "values.weight"]) + print([(job.values["tube1"], job.values["weight"]) for job in jobs]) In general, query parameters support the following operators: ``lt`` (less than), ``le`` (less or equal), ``=`` (equal), ``ne`` (not equal), ``ge`` (greater or equal), ``gt`` (greater than), ``in`` (value found in list) and @@ -97,18 +113,18 @@ In general, query parameters support the following operators: ``lt`` (less than) .. code-block:: python # Equal - jobs = project.get_jobs(eval_status="evaluated") + jobs = project_api.get_jobs(eval_status="evaluated") # In - jobs = project.get_jobs(eval_status=["prolog", "running"]) + jobs = project_api.get_jobs(eval_status=["prolog", "running"]) # Contains query_params = {"note.contains": "search_string"} - jobs = project.get_jobs(**query_params) + jobs = project_api.get_jobs(**query_params) # Less than query_params = {"fitness.lt": 1.8} - jobs = project.get_jobs(**query_params) + jobs = project_api.get_jobs(**query_params) Objects vs dictionaries ----------------------------------- @@ -121,26 +137,27 @@ such as ``Numpy``, ``Pandas``, etc. import pandas - project = client.get_project(id="mapdl_motorbike_frame") + project = jms_api.get_project_by_name(name="Mapdl Motorbike Frame") # Get parameter values for all evaluated design points - jobs = project.get_jobs(fields=["id", "values"], eval_status="evaluated", as_objects=False) + jobs = project_api.get_jobs(fields=["id", "values"], eval_status="evaluated", as_objects=False) # Import jobs data into a flat DataFrame - df = pandas.io.json.json_normalize(jobs) + df = pandas.json_normalize(jobs) # Output - # id values.tube1_radius values.tube1_thickness values.tube2_radius values.tube2_thickness values.tube3_radius values.tube3_thickness ... values.tube15 values.tube16 values.tube17 values.tube18 values.tube19 values.tube20 values.tube21 - # 0 1 7.055903 0.728247 17.677894 0.512761 13.342691 0.718970 ... 1 3 2 3 1 1 1 - # 1 2 18.172368 2.407453 9.216933 0.818597 11.789593 1.439845 ... 3 1 3 2 3 3 2 - # 2 3 14.832407 2.380437 7.484620 1.601617 19.742424 0.816099 ... 2 1 1 1 2 2 3 - # 3 4 10.254875 2.420485 10.429973 2.241802 14.647943 0.501836 ... 1 3 2 1 3 3 3 - # 4 5 14.601405 1.657524 10.056457 1.743385 8.821876 2.200616 ... 1 2 3 3 2 1 2 - # 5 6 10.393178 2.155777 8.043999 2.036772 11.605410 2.426192 ... 3 1 1 1 2 1 1 - # 6 7 10.415530 1.675479 4.570576 1.461735 16.915658 1.822555 ... 3 3 3 2 1 1 2 - # 7 8 12.841433 1.322097 6.142197 1.659299 6.275559 2.312346 ... 3 2 2 3 1 1 3 - # 8 9 18.394536 2.446091 12.882719 0.939273 15.167834 1.683604 ... 3 1 2 3 2 2 1 - # 9 10 12.414343 1.699816 6.128372 1.314386 18.783781 1.736996 ... 1 3 2 1 3 1 2 + # id values.mapdl_cp_time values.mapdl_elapsed_time values.mapdl_elapsed_time_obtain_license values.max_stress ... values.tube6 values.tube7 values.tube8 values.tube9 values.weight + # 0 02qoqedl8QCjkuLcqCi10Q 0.500 24.0 21.9 1010.256091 ... 3 1 1 2 3.027799 + # 1 02qoqedlDMO1LrSGoHQqnT 0.406 23.0 21.5 227.249112 ... 2 3 3 2 11.257201 + # 2 02qoqedlApzJZd7fQSQIJg 0.438 24.0 21.2 553.839050 ... 3 2 1 2 6.358393 + # 3 02qoqedlGMYZi7YBive78D 0.469 25.0 22.9 162.944726 ... 1 1 1 3 9.919099 + # 4 02qoqedlKBzRz939iDCCex 0.391 25.0 22.6 218.976121 ... 3 2 2 2 6.884490 + # 5 02qoqedlLfvwuA4uaf5GKR 0.406 24.0 22.4 455.888101 ... 1 3 1 2 7.346944 + # 6 02qoqedlLvoSgPoLxla8F9 0.391 27.0 25.2 292.885562 ... 1 1 1 3 6.759635 + # 7 02qoqedlOKg8Vg5AlTrji6 0.484 28.0 26.2 377.721100 ... 1 1 3 2 5.952097 + # 8 02qoqedlRtDwuw2uTQ99Vq 0.469 28.0 25.9 332.336753 ... 1 3 2 2 7.463696 + # 9 02qoqedlPYyGRTivqB5vxf 0.453 27.0 25.5 340.147675 ... 3 2 2 3 6.631538 + # 10 02qoqedlN1ebRV77zuUVYd 0.453 28.0 25.5 270.691391 ... 2 2 1 3 8.077236 Set failed design points to pending @@ -150,14 +167,14 @@ Query a specific project and set its failed design points (if any) to pending. .. code-block:: python - project = client.get_project(id="mapdl_motorbike_frame") - jobs = project.get_jobs() + project = jms_api.get_project_by_name(name="Mapdl Motorbike Frame") + jobs = project_api.get_jobs() - failed_dps = [dp for dp in jobs if dp.eval_status == "failed"] + failed_jobs = [job for job in jobs if job.eval_status == "failed"] - for dp in failed_dps: - dp.eval_status = "pending" - failed_dps = project.update_jobs(failed_dps) + for job in failed_jobs: + job.eval_status = "pending" + failed_jobs = project_api.update_jobs(failed_jobs) Modify a project job_definition @@ -167,17 +184,29 @@ Query an existing project job_definition, modify it and send it back to the serv .. code-block:: python - project = client.get_project(id="mapdl_motorbike_frame") + project = jms_api.get_project_by_name(name="Mapdl Motorbike Frame") # get currently active job_definition - job_def = project.get_job_definitions(active=True)[0] + job_def = project_api.get_job_definitions(active=True)[0] # Update the lower limit of a parameter - parameter = job_def.parameter_definitions[0] - parameter.lower_limit = 2.5 + parameter_id = job_def.parameter_definition_ids[0] + parameter_def = project_api.get_parameter_definitions(id=parameter_id)[0] + print(parameter_def) + # { + # "id": "02qoqeciKZxk3Ua4QjPwue", + # "name": "tube1_radius", + # "mode": "input", + # "type": "float", + # "default": 12.0, + # "lower_limit": 4.0, + # "upper_limit": 20.0, + # "cyclic": false + # } + parameter_def.lower_limit = 2.5 # send the updated job_definition to the server - job_def = project.update_job_definitions([job_def])[0] + project_api.update_parameter_definitions([parameter_def]) Delete some design points @@ -187,10 +216,10 @@ Query and then delete all design points that timed out. .. code-block:: python - project = client.get_project(id="mapdl_motorbike_frame") + project = jms_api.get_project_by_name(name="Mapdl Motorbike Frame") - jobs = project.get_jobs(fields=['id'], eval_status="timeout") - project.delete_jobs(jobs) + jobs = project_api.get_jobs(fields=['id'], eval_status="timeout") + project_api.delete_jobs(jobs) Query the number of evaluators @@ -198,7 +227,7 @@ Query the number of evaluators .. code-block:: python - evaluators = client.get_evaluators() + evaluators = jms_api.get_evaluators() # print number of Windows and Linux evaluators connected to the DCS server print( len([e for e in evaluators if e.platform == "Windows" ]) ) @@ -208,15 +237,15 @@ Query the number of evaluators Replace a file in a project ------------------------------------------ -Get file definitions from an existing project job_definition and replace the first one. +Get file definitions from an existing project Job Definition and replace the first one. .. code-block:: python - job_def = project.get_job_definitions(active=True)[0] - files = project.get_files() + job_def = project_api.get_job_definitions(active=True)[0] + files = project_api.get_files() file = files[0] - file.src = r"D:\local_folder\my_project\workbench_archive.wbpz" - files = project.update_files([file]) + file.src = r"D:\local_folder\my_project\input_file.xyz" + project.update_files([file]) For instructions on how to add a new file to an existing project job_definition, see :ref:`Adding a file to a project `. @@ -230,7 +259,7 @@ Users with admin rights (such as the default ``repadmin`` user) can create new u from ansys.rep.client import Client from ansys.rep.client.auth import AuthApi, User - client = Client(rep_url="https://127.0.0.1:8443/rep/", username="repadmin", password="repadmin") + client = Client(rep_url="https://localhost:8443/rep/", username="repadmin", password="repadmin") auth_api = AuthApi(client) # modify the default password of the repadmin user @@ -243,38 +272,48 @@ Users with admin rights (such as the default ``repadmin`` user) can create new u email='test_user@test.com', fullname='Test User', is_admin=False) new_user = auth_api.create_user(new_user) + print(new_user) + # { + # "id": "f9e068d7-4962-45dc-92a4-2273246039da", + # "username": "test_user", + # "email": "test_user@test.com" + # } + new_user.password = "new_password" + auth_api.update_user(new_user) Exception handling ------------------------------------------ -All exceptions that the Ansys DCS clients explicitly raise inherit from :exc:`ansys.rep.client.REPError`. +All exceptions that the Ansys REP client explicitly raise inherit from :exc:`ansys.rep.client.REPError`. Client Errors are raised for 4xx HTTP status codes, while API Errors are raised for 5xx HTTP status codes (server side errors). -For example, instantiating a client with invalid credentials will return a 400 Client Error. +For example, instantiating a client with invalid credentials will return a 401 Client Error. .. code-block:: python - from ansys.rep.client import REPError - from ansys.rep.client.jms import Client + from ansys.rep.client import Client, REPError try: - client = Client(rep_url="https://127.0.0.1:8443/rep/", username="repadmin", password="wrong_psw") + client = Client(rep_url="https://localhost:8443/rep/", username="repadmin", password="wrong_psw") except REPError as e: print(e) #Output: - #400 Client Error: invalid_grant for: POST https://127.0.0.1:8443/rep/auth/api/oauth/token - #Invalid "username" or "password" in request. + # 401 Client Error: invalid_grant for: POST https://localhost:8443/rep/auth/realms/rep/protocol/openid-connect/token + # Invalid user credentials -A *get* call on a non-existing resource will return a 404 Client Error. +A *get* call on a non-existing resource will return a 404 Client Error. .. code-block:: python + from ansys.rep.client.jms import JmsApi + + jms_api = JmsApi(client) try: - client.get_project(id="non_existing_project") + jms_api.get_project(id="non_existing_project") except REPError as e: print(e) #Output: - #404 Client Error: Not Found for: GET https://127.0.0.1:8443/rep/dps/api//projects/non_existing_project \ No newline at end of file + #404 Client Error: Not Found for: GET https://localhost:8443/rep//jms/api/v1/projects/non_existing_project \ No newline at end of file diff --git a/examples/mapdl_motorbike_frame/project_setup.py b/examples/mapdl_motorbike_frame/project_setup.py index 7d5bc2a7e..45180d7d4 100644 --- a/examples/mapdl_motorbike_frame/project_setup.py +++ b/examples/mapdl_motorbike_frame/project_setup.py @@ -326,7 +326,7 @@ def create_project(client, name, num_jobs=20, use_exec_script=False) -> Project: if __name__ == "__main__": parser = argparse.ArgumentParser() - parser.add_argument("-n", "--name", type=str, default="mapdl_motorbike_frame") + parser.add_argument("-n", "--name", type=str, default="Mapdl Motorbike Frame") parser.add_argument("-j", "--num-jobs", type=int, default=50) parser.add_argument("-es", "--use-exec-script", default=False, action="store_true") parser.add_argument("-U", "--url", default="https://127.0.0.1:8443/rep") From ec70626f62662fa05161dd17c541256427ba3829 Mon Sep 17 00:00:00 2001 From: Federico Negri Date: Mon, 29 Aug 2022 17:54:12 +0200 Subject: [PATCH 2/5] Update doc index and installation --- doc/source/conf.py | 111 ++++++++--------------------------------- doc/source/index.rst | 35 ++++++------- doc/source/install.rst | 25 ++++------ 3 files changed, 47 insertions(+), 124 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 3b81fad29..0ac3a99db 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # Sphinx documentation configuration file from datetime import datetime import os @@ -11,16 +9,22 @@ sys.path.append(os.path.abspath(os.path.dirname(__file__))) +# -- Project information ----------------------------------------------------- + +# General information about the project. +project = "Ansys pyrep" +copyright = f"(c) {datetime.now().year} ANSYS, Inc. All rights reserved" +author = __company__ + +# The short X.Y version +release = version = __version__ + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) -# -- General job_definition ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# needs_sphinx = '1.0' - +# -- General configuration --------------------------------------------------- extensions = [ "sphinx.ext.autodoc", "sphinx.ext.autosummary", @@ -62,23 +66,9 @@ # The master toctree document. master_doc = "index" -# General information about the project. -project = "Ansys pyrep" -copyright = f"(c) {datetime.now().year} {__company__}. All rights reserved" -author = __company__ - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = __external_version__ -# The full version, including alpha/beta/rc tags. -release = __external_version__ - # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -# language = None +language = "en" # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: @@ -121,106 +111,45 @@ # -- Options for HTML output ---------------------------------------------- -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. html_theme = "ansys_sphinx_theme" # only for sphinx_rtd_theme html_theme_options = { - "github_url": "[https://github.com/pyansys/pyrep", + "github_url": "https://github.com/pyansys/pyrep", "show_prev_next": False, "show_breadcrumbs": True, "additional_breadcrumbs": [ ("PyAnsys", "https://docs.pyansys.com/"), ], - #'canonical_url': '', - #'analytics_id': 'UA-XXXXXXX-1', # Provided by Google in your dashboard - #'logo_only': False, - # "display_version": True, - # "prev_next_buttons_location": "bottom", - #'style_external_links': False, - #'vcs_pageview_mode': '', - #'style_nav_header_background': 'white', - # Toc options "collapse_navigation": True, - # "sticky_navigation": True, "navigation_depth": 4, - # "includehidden": True, - # "titles_only": False, } -# Add any paths that contain custom themes here, relative to this directory. -# html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -# html_title = None - # A shorter title for the navigation bar. Default is the same as html_title. -html_short_title = html_title = "pyrep" +html_short_title = html_title = "PyREP" # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = logo -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# html_favicon = None +# Favicon +html_favicon = "favicon.png" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -# html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} - -# If false, no module index is generated. -# html_domain_indices = True - -# If false, no index is generated. -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# html_split_index = False - # If true, links to the reST sources are added to the pages. html_show_sourcelink = False -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -# html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -# html_show_copyright = True - # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' -# This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None - # Output file base name for HTML help builder. -htmlhelp_basename = "REPclientdoc" +htmlhelp_basename = "pyrepdoc" # -- Options for LaTeX output --------------------------------------------- @@ -242,7 +171,7 @@ "index", "ansys-rep-client.tex", "REP Python Client Documentation", - "ANSYS Switzerland Gmbh", + author, "manual", ), ] @@ -343,9 +272,9 @@ secured?returnurl=/Views/Secured/corp/v231/en/rep_ug/%s.html""", "ANSYS Help - ", ), - "ansys_rep_tutorial": ( + "ansys_dcs_tutorial": ( """https://ansyshelp.ansys.com/account/ - secured?returnurl=/Views/Secured/corp/v231/en/rep_tut/%s.html""", + secured?returnurl=/Views/Secured/corp/v231/en/dcs_tut/%s.html""", "REP Tutorial - ", ), } diff --git a/doc/source/index.rst b/doc/source/index.rst index c229007b3..bbec0103f 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -1,28 +1,25 @@ -Ansys REP Python Client -============================= +PyREP Documentation |version| +================================= + +.. toctree:: + :hidden: + :maxdepth: 3 + + install + quickstart + examples/index + api/index + +Introduction +------------ Ansys Remote Execution Platform (REP) is a family of applications that enables you to distribute, manage and solve simulations on a variety of compute resources. -As part of this, the Job Management Service (JMS) facilitates the robust solution of -tens of thousands of design points spread across clusters, networks and operating systems. - -**ansys-rep-client** brings Ansys REP to your Python application. +``ansys-rep-client`` brings Ansys REP to your Python application. Wrapping around the REP REST APIs, it allows you to: * create new projects and modify existing ones * monitor and manage jobs * run your own design exploration algorithms -* retrieve simulation results - -User Guide -=========== - -.. toctree:: - :maxdepth: 2 - - install - quickstart - examples/index - api/index - changelog/index \ No newline at end of file +* retrieve simulation results \ No newline at end of file diff --git a/doc/source/install.rst b/doc/source/install.rst index 0aeae62ba..3c3f70b37 100644 --- a/doc/source/install.rst +++ b/doc/source/install.rst @@ -5,40 +5,37 @@ Installation Make sure you have ``Python 3`` and that the expected version is available from your command line. You can check this by running: -.. parsed-literal:: +.. code:: - $ python --version + python --version If you do not have Python, please install the latest 3.x version from `python.org `_. Additionally, make sure you have ``pip`` available. You can check this by running: -.. parsed-literal:: +.. code:: - $ pip --version + pip --version If pip isn't already installed, please refer to the `Installing Packages Tutorial `_ from the Python Packaging Authority. -Installing ``ansys-dcs-client`` is as simple as, on Windows: +As long as PyREP is a private PyAnsys package not published to PyPI yet, you can execute -.. parsed-literal:: - - $ pip install "%AWP_ROOT\ |version_no_dots|\ %\\dcs\\share\\python_client\\ansys_dcs_client-\ |client_version|\ -py3-none-any.whl" - -on Linux: - -.. parsed-literal:: - - $ pip install /usr/ansys_inc/v\ |version_no_dots|\ /dcs/share/python_client/ansys_dcs_client-\ |client_version|\ -py3-none-any.whl +.. code:: + python -m pip install git+https://github.com/pyansys/pyrep The following dependencies are automatically installed through ``pip`` (if not already available): +- cachetools_ - requests_ - marshmallow_ - marshmallow_oneofschema_ +- python-keycloak_ .. _requests: https://pypi.org/project/requests/ .. _marshmallow: https://pypi.org/project/marshmallow/ .. _marshmallow_oneofschema: https://pypi.org/project/marshmallow-oneofschema/ +.. _cachetools: https://pypi.org/project/cachetools/ +.. _python-keycloak: https://pypi.org/project/python-keycloak/ From 9d8d4bdab281897e847b8d217b33b194a4e81830 Mon Sep 17 00:00:00 2001 From: Federico Negri Date: Mon, 29 Aug 2022 17:54:57 +0200 Subject: [PATCH 3/5] Improve json style printing of objects --- ansys/rep/client/jms/resource/base.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/ansys/rep/client/jms/resource/base.py b/ansys/rep/client/jms/resource/base.py index 5c3474d19..a214e679b 100644 --- a/ansys/rep/client/jms/resource/base.py +++ b/ansys/rep/client/jms/resource/base.py @@ -55,4 +55,24 @@ def __repr__(self): ) def __str__(self): - return json.dumps(self.Meta.schema(many=False).dump(self), indent=2) + + # Ideally we'd simply do + # return json.dumps(self.Meta.schema(many=False).dump(self), indent=2) + # However the schema.dump() function (rightfully) ignores fields marked as load_only. + # + # Therefore we have to manually iterate over all fields + + schema = self.Meta.schema(many=False) + dict_repr = schema.dict_class() + for attr_name, field_obj in schema.fields.items(): + value = missing + try: + value = field_obj.serialize(attr_name, self, accessor=schema.get_attribute) + except: + pass + if value is missing: + continue + key = field_obj.data_key if field_obj.data_key is not None else attr_name + dict_repr[key] = value + + return json.dumps(dict_repr, indent=2) From 5a98031326a1aaefb82c2bb0c047c5f87282bd87 Mon Sep 17 00:00:00 2001 From: Federico Negri Date: Mon, 29 Aug 2022 18:35:47 +0200 Subject: [PATCH 4/5] Update tests --- tests/jms/test_jms_api.py | 2 +- tests/jms/test_project_permissions.py | 2 +- tests/jms/test_projects.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/jms/test_jms_api.py b/tests/jms/test_jms_api.py index 49f0850af..05dfe7ed5 100644 --- a/tests/jms/test_jms_api.py +++ b/tests/jms/test_jms_api.py @@ -24,7 +24,7 @@ def test_jms_api(self): log.debug("=== Client ===") client = self.client() - proj_name = "mapdl_motorbike_frame" + proj_name = "Mapdl Motorbike Frame" log.debug("=== Projects ===") jms_api = JmsApi(client) diff --git a/tests/jms/test_project_permissions.py b/tests/jms/test_project_permissions.py index 147d43190..08354d934 100644 --- a/tests/jms/test_project_permissions.py +++ b/tests/jms/test_project_permissions.py @@ -148,7 +148,7 @@ def test_modify_project_permissions(self): root_api2 = JmsApi(client2) log.info(f"Client connected at {client2.rep_url} with user {user2.username}") - proj_user2 = root_api2.get_project(name=proj_name) + proj_user2 = root_api2.get_project_by_name(name=proj_name) project_api2 = ProjectApi(client2, proj_user2.id) add_job_definition_to_project(project_api2, f"Config 2 - {user2.username}") diff --git a/tests/jms/test_projects.py b/tests/jms/test_projects.py index 2ca02089f..db2ac63de 100644 --- a/tests/jms/test_projects.py +++ b/tests/jms/test_projects.py @@ -165,12 +165,12 @@ def test_project_copy(self): tgt_name = proj_name + "_copy1" project_api = ProjectApi(client, proj.id) proj1_id = project_api.copy_project(tgt_name) - copied_proj1 = jms_api.get_project(name=tgt_name) + copied_proj1 = jms_api.get_project_by_name(name=tgt_name) self.assertIsNotNone(copied_proj1) tgt_name = proj_name + "_copy2" project_api.copy_project(tgt_name) - copied_proj2 = jms_api.get_project(name=tgt_name) + copied_proj2 = jms_api.get_project_by_name(name=tgt_name) self.assertIsNotNone(copied_proj2) # Delete projects From 079bdb8bb49ccb56c727924dc72fc119ad3772b7 Mon Sep 17 00:00:00 2001 From: Federico Negri Date: Mon, 29 Aug 2022 23:07:21 +0200 Subject: [PATCH 5/5] Add missing file --- doc/source/favicon.png | Bin 0 -> 6657 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/source/favicon.png diff --git a/doc/source/favicon.png b/doc/source/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..c755f53d57afddacf708c5e16c28fbcf449ddee9 GIT binary patch literal 6657 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 Jlqp%i{U2eu!V3TZ literal 0 HcmV?d00001