diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b01ed31..fa2450b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,16 +8,24 @@ on: branches: [ release ] workflow_dispatch: +# Add permission to write GitHub pages +permissions: + contents: write + pages: write + id-token: write + jobs: test: strategy: fail-fast: false matrix: - MATLABVersion: [R2023b,R2024a] + MATLABVersion: [R2024b,R2025a,R2025b] runs-on: ubuntu-latest + env: + LD_PRELOAD: /usr/lib/x86_64-linux-gnu/libstdc++.so.6 steps: # Checks-out your repository - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # Sets up a display server - name: Start display server @@ -32,9 +40,17 @@ jobs: uses: matlab-actions/setup-matlab@v2 with: release: ${{ matrix.MATLABVersion }} - products: Symbolic_Math_Toolbox + products: > + Symbolic_Math_Toolbox # Simulink Statistics_and_Machine_Learning_Toolbox # List required products above in the format shown (and uncomment them) + # List of product strings: + # Simulink + # Statistics_and_Machine_Learning_Toolbox + # Simulink_Coder + # Econometrics_Toolbox + # Deep_Learning_Toolbox + # Run all the tests - name: Run SmokeTests @@ -44,10 +60,12 @@ jobs: # Upload the test results as artifact - name: Upload TestResults - uses: actions/upload-artifact@v3.1.3 + if: ${{ always() }} + uses: actions/upload-artifact@v4 with: - name: TestResults - path: ./SoftwareTests/TestResults_${{ matrix.MATLABVersion }}.txt + name: TestResults_${{ matrix.MATLABVersion }} + path: ./public/* + overwrite: true badge: if: ${{ always() }} @@ -58,26 +76,38 @@ jobs: steps: # Checks-out your repository - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # Sets up R2023b - name: Setup MATLAB uses: matlab-actions/setup-matlab@v2 with: - release: R2023b + release: R2024b # Download the test results from artifact - - name: Download TestResults - uses: actions/download-artifact@v2.1.1 + - name: Download All TestResults + uses: actions/download-artifact@v4 with: - name: TestResults - path: ./SoftwareTests/ - + path: public + pattern: TestResults_* + merge-multiple: true + # Create the test results badge - - name: Run CreateBadge + - name: Run PostSmokeTest uses: matlab-actions/run-command@v2 with: - command: openProject(pwd); CreateBadge; + command: openProject(pwd); PostSmokeTest; + + # Deploy reports to GitHub pages + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: public + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 # Commit the JSON for the MATLAB releases badge - name: Commit changed files @@ -85,8 +115,6 @@ jobs: run: | git config user.name "${{ github.workflow }} by ${{ github.actor }}" git config user.email "<>" - git pull git add Images/TestedWith.json git commit Images/TestedWith.json -m "Update CI badges ${{ github.ref_name }}" - git fetch git push diff --git a/.gitignore b/.gitignore index aef55c1..14f9f76 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,4 @@ # List of untracked files to ignore -*.vs - -# Prevent accidentally committing your API key -myAPIkey.txt - -# Live Task Repository -MATLAB-Live-Task-for-Python # Autosave files *.asv @@ -13,6 +6,9 @@ MATLAB-Live-Task-for-Python *.autosave *.slx.r* *.mdl.r* +__pycache__ +*.pyc +*/*.pyc # MATLAB Drive *.MATLABDriveTag @@ -47,9 +43,6 @@ codegen/ *.elf *.hex *.bin -__pycache__ -*.pyc -*/*.pyc # Cache files *.slxc @@ -57,8 +50,12 @@ __pycache__ # Project settings Utilities/ProjectSettings.mat -# Test results -SoftwareTests/TestResults_* - # GitLab page folder public/ + +# Prevent accidentally committing your API key +myAPIkey.txt + +# Live Task Repository +MATLAB-Live-Task-for-Python + diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 8d2d7db..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,98 +0,0 @@ -stages: -# Set up two testing paths - - setup - - test - - deploy - - release - -setup-job: - tags: - - matlab - stage: setup - script: - - cd .. - - if (test-path utilities) { rm -r -force utilities } - - git clone git@insidelabs-git.mathworks.com:modular-curriculum-content/utilities.git - - cd $CI_PROJECT_NAME - allow_failure: false - - -smoke-test: -# Smoke tests should run all the time - tags: - # Add additional tags like (e.g. - arduino) as required - # Make sure that the runner you plan to use matches the tags - - matlab - stage: test - script: - - matlab -batch "openProject(pwd);RunAllTests(true)" - when: always - allow_failure: true - artifacts: - paths: - - public/* - expire_in: 1 years - -pages: - tags: - - matlab - stage: deploy - script: - - echo 'Deploying pages' - artifacts: - paths: - - public - -smoke-test-solution: - tags: - - matlab - stage: release - script: - - matlab -batch "proj = openProject(pwd); - addpath(genpath(proj.RootFolder)); - results = runtests(fullfile('InternalFiles','Tests','CI','SolnSmokeTests.m')); - disp(table(results)); assertSuccess(results);" - rules: -# This test should always run when merging to main -# And be available for manual running on any push - - if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH - when: always - - if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME != $CI_DEFAULT_BRANCH - when: manual - allow_failure: true - -file-test: - tags: - - matlab - stage: release - script: - - matlab -batch "proj = openProject(pwd); - addpath(proj.RootFolder+'/InternalFiles/Tests/CI'); - results = runtests('OpenCloseFileTest.m'); - disp(table(results)); assertSuccess(results);" - rules: -# This test should always run when merging to main -# And be available for manual running on any push - - if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH - when: always - - if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME != $CI_DEFAULT_BRANCH - when: manual - allow_failure: true - -release-testing: - tags: - - matlab - stage: release - script: - - matlab -batch "proj = openProject(pwd); - cd ..; - addpath(genpath(fullfile('utilities','TestingResources'))); - runCMTests" - rules: -# This test should always run when merging to main -# And be available for manual running on any push - - if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH - when: always - - if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME != $CI_DEFAULT_BRANCH - when: manual - allow_failure: true diff --git a/.vs/ProjectSettings.json b/.vs/ProjectSettings.json deleted file mode 100644 index f8b4888..0000000 --- a/.vs/ProjectSettings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "CurrentProjectSetting": null -} \ No newline at end of file diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite deleted file mode 100644 index 53d9b1b..0000000 Binary files a/.vs/slnx.sqlite and /dev/null differ diff --git a/FunctionLibrary/.gitkeep b/FunctionLibrary/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/FunctionLibrary/CheckPythonVersion.mlx b/FunctionLibrary/CheckPythonVersion.mlx index 8095c4b..b5e0ab7 100644 Binary files a/FunctionLibrary/CheckPythonVersion.mlx and b/FunctionLibrary/CheckPythonVersion.mlx differ diff --git a/Images/image_6.png b/Images/MouseThoughtBubble.png similarity index 100% rename from Images/image_6.png rename to Images/MouseThoughtBubble.png diff --git a/Images/image_9.png b/Images/RGBTurkeys.png similarity index 100% rename from Images/image_9.png rename to Images/RGBTurkeys.png diff --git a/Images/image_12.png b/Images/SeaSurfaceTemps.png similarity index 100% rename from Images/image_12.png rename to Images/SeaSurfaceTemps.png diff --git a/Images/TestedWith.json b/Images/TestedWith.json index 61fdf9a..7e7e1c0 100644 --- a/Images/TestedWith.json +++ b/Images/TestedWith.json @@ -1 +1 @@ -{"schemaVersion":1,"label":"Tested with","color":"success","message":"R2023b | R2024a"} +{"schemaVersion":1,"label":"Test Status","color":"success","message":"R2024b | R2025a | R2025b"} diff --git a/Images/image_5.png b/Images/WeatherDashboard.png similarity index 100% rename from Images/image_5.png rename to Images/WeatherDashboard.png diff --git a/Images/image_4.png b/Images/WeatherStruct.png similarity index 100% rename from Images/image_4.png rename to Images/WeatherStruct.png diff --git a/InstructorResources/Solutions/CheckingTheWeatherSoln.mlx b/InstructorResources/Solutions/CheckingTheWeatherSoln.mlx new file mode 100644 index 0000000..c81f902 Binary files /dev/null and b/InstructorResources/Solutions/CheckingTheWeatherSoln.mlx differ diff --git a/InstructorResources/Solutions/CreateCurrentWeatherAppSoln.mlx b/InstructorResources/Solutions/CreateCurrentWeatherAppSoln.mlx new file mode 100644 index 0000000..acb7550 Binary files /dev/null and b/InstructorResources/Solutions/CreateCurrentWeatherAppSoln.mlx differ diff --git a/InstructorResources/Solutions/UsingMATLABwithPythonSoln.mlx b/InstructorResources/Solutions/UsingMATLABwithPythonSoln.mlx new file mode 100644 index 0000000..f286f6c Binary files /dev/null and b/InstructorResources/Solutions/UsingMATLABwithPythonSoln.mlx differ diff --git a/MainMenu.mlx b/MainMenu.mlx index dc07f4c..d9f6355 100644 Binary files a/MainMenu.mlx and b/MainMenu.mlx differ diff --git a/README.md b/README.md index 75c9acc..f2b2f91 100644 --- a/README.md +++ b/README.md @@ -1,144 +1,117 @@ - - - -# Programming: A Starter Project Using MATLAB with Python - - -[![View on File Exchange](https://www.mathworks.com/matlabcentral/images/matlab-file-exchange.svg)](https://www.mathworks.com/matlabcentral/fileexchange/116490-programming-a-starter-project-using-matlab-and-python) or [![Open in MATLAB Online](https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg)](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python&project=MATLABwithPython.prj) - -![MATLAB Versions Tested](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2FMathWorks-Teaching-Resources%2FProgramming-A-Starter-Project-Using-MATLAB-and-Python%2Frelease%2FImages%2FTestedWith.json) - -**Curriculum Module** - -_Created with R2022a. Compatible with R2022a and later releases._ - -# Information - -This curriculum module contains interactive [MATLAB® live scripts](https://www.mathworks.com/products/matlab/live-editor.html) that teach fundamental concepts and basic terminology related to programming computers. - -# Prerequisite Domain Knowledge - -This module assumes familiarity with basic programming concepts such as variables, data types, and functions, structures including arrays and structs, and control flows including if/else as well as how to use them in MATLAB. These ideas are all presented with interactive examples in [Fundamentals of Programming](https://www.mathworks.com/matlabcentral/fileexchange/103225-fundamentals-of-programming), [Programming: Organizing Data](https://www.mathworks.com/matlabcentral/fileexchange/115900-programming-organizing-data), [Programming: Structuring Code](https://www.mathworks.com/matlabcentral/fileexchange/115905-programming-structuring-code). - - - -## Background - -You can use these live scripts as demonstrations in lectures, class activities, or interactive assignments outside class. This module explores interactions between systems by using the OpenWeather API and calling into Python from MATLAB. **Programming: A Starter Project Using MATLAB with Python** covers using the Run Python Code Live Task to run provided Python code, accessing the OpenWeather API to check the weather, extracting useful data from the API call, and using App Designer to create an app with a personalized weather display. - - -The instructions inside the live scripts will guide you through the exercises and activities. Get started with each live script by running it one section at a time. To stop running the script or a section midway (for example, when an animation is in progress), use the EndIcon.png Stop button in the **RUN** section of the **Live Editor** tab in the MATLAB Toolstrip. - -## Contact Us - -Solutions are available upon instructor request. Contact the [MathWorks teaching resources team](mailto:onlineteaching@mathworks.com) if you would like to request solutions, provide feedback, or if you have a question. - - - -## Prerequisites - -This module assumes familiarity with basic programming concepts such as variables, data types, and functions, structures including arrays and structs, and control flows including if/else as well as how to use them in MATLAB. These ideas are all presented with interactive examples in [Fundamentals of Programming](https://github.com/MathWorks-Teaching-Resources/Fundamentals-of-Programming), [Programming: Organizing Data](https://github.com/MathWorks-Teaching-Resources/Programming-Organizing-Data), [Programming: Structuring Code](https://github.com/MathWorks-Teaching-Resources/Programming-Structuring-Code). - - - -## Getting Started -### Accessing the Module -### **On MATLAB Online:** - -Use the [OpenInMO.png](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python&project=MATLABwithPython.prj) link to download the module. You will be prompted to log in or create a MathWorks account. The project will be loaded, and you will see an app with several navigation options to get you started. - -### **On Desktop:** - -Download or clone this repository. Open MATLAB, navigate to the folder containing these scripts and double\-click on [MATLABwithPython.prj](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python&project=MATLABwithPython.prj). It will add the appropriate files to your MATLAB path and open an app that asks you where you would like to start. - - -Ensure you have all the required products ([listed below](#H_E850B4FF)) installed. If you need to include a product, add it using the Add\-On Explorer. To install an add\-on, go to the **Home** tab and select AddOnsIcon.png **Add-Ons** > **Get Add-Ons**. - - - -## Products - -MATLAB® is used throughout. Tools from the Symbolic Math Toolbox™ are used to convert between different unit systems in the weather applications. - - - -# Scripts - - *If you are viewing this in a version of MATLAB prior to R2023b, you can view the learning outcomes for each script* [*here*](https://github.com/MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python)*.* - - - -## [**UsingMATLABwithPython.mlx**](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python&project=MATLABwithPython.prj&file=UsingMATLABwithPython.mlx) -| **Introductory script**
| **In this script, students will...**
| -| :-- | :-- | -| RunPythonLiveTask.png
|
- check that an appropriate version of Python is installed and visible within MATLAB

- explore the Run Python Code Live Task

- try running Python commands and a Python script from MATLAB
| - - - -## [**CheckingTheWeather.mlx**](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python&project=MATLABwithPython.prj&file=CheckingTheWeather.mlx) -| **Investigatory script**
| **In this script, students will...**
| -| :-- | :-- | -| image_4.png
|
- set up an account with OpenWeather to create your own API key

- use existing Python code to make an API call to OpenWeather

- use MATLAB to explore the data returned by the API call, including data type conversions and unit conversions
| - - - -## [**CreateCurrentWeatherApp.mlx**](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python&project=MATLABwithPython.prj&file=CreateCurrentWeatherApp.mlx) -| **Application script**
| **In this script, students will...**
| -| :-- | :-- | -| image_5.png
|
- use App Designer to build a custom app using the code from CheckingTheWeather.mlx

- create callbacks, properties, and functions

- design and organize a UI with drag and drop elements

- reuse code from CheckingTheWeather in a new context
| - - - -# Apps - -Both of these apps require running Python from MATLAB, as set up in [UsingMATLABwithPython](#H_00FC9291) and an OpenWeather API key, as set up in [CheckingTheWeather](#H_39C74124). - -- **CurrentWeatherAppDemo.mlapp** shows the results of working through CreateCurrentWeatherApp. -- **WeatherDisplay.mlapp** shows one elaborated version of the basic current weather app. - - -# Additional Scripts - -For those with a legacy OpenWeather API key, or the willingness to sign up for the OneCall 3.0 API, the original weather forecasting exploration from this courseware module is included as well. - - -[WeatherForecast.mlx](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python&project=MATLABwithPython.prj&file=WeatherForecast.mlx) - - -[SampleWeatherDashboard.mlx](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python&project=MATLABwithPython.prj&file=SampleWeatherDashboard.mlx) - - - -# License - -The license for this module is available in the [LICENSE.md](https://github.com/MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python/blob/release/LICENSE.md). - - - -# Related Courseware Modules -| **Courseware Module**
| **Sample Content**
| **Available on:**
| -| :-- | :-- | :-- | -| [**Fundamentals of Programming**](https://www.mathworks.com/matlabcentral/fileexchange/103225-fundamentals-of-programming)
Learn the basics of how to make a computer
accept, store, and compute with information
| image_6.png
| [OpenInFX.png](https://www.mathworks.com/matlabcentral/fileexchange/103225-fundamentals-of-programming)
[OpenInMO.png](http://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Fundamentals-of-Programming&project=FundamentalsofProgramming.prj)
[GitHub](https://github.com/MathWorks-Teaching-Resources/Fundamentals-of-Programming)
| -| [**Programming: Structuring Code**](https://www.mathworks.com/matlabcentral/fileexchange/115905-programming-structuring-code)
Learn how to organize your code into functions,
debug, comment, and share
| image_9.png
| [OpenInFX.png](https://www.mathworks.com/matlabcentral/fileexchange/115905-programming-structuring-code)
[OpenInMO.png](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Programming-Structuring-Code&project=StructuringCode.prj)
[GitHub](https://github.com/MathWorks-Teaching-Resources/Programming-Structuring-Code)
| -|
[**Programming: Organizing Data**](https://www.mathworks.com/matlabcentral/fileexchange/115900-programming-organizing-data)
Learn more about strings, numeric data types,
memory, and ways of storing data
| image_12.png
| [OpenInFX.png](https://www.mathworks.com/matlabcentral/fileexchange/115900-programming-organizing-data)
[OpenInMO.png](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Programming-Organizing-Data&project=OrganizingData.prj)
[GitHub](https://github.com/MathWorks-Teaching-Resources/Programming-Organizing-Data)
| - - -Or feel free to explore our other [modular courseware content](https://www.mathworks.com/matlabcentral/fileexchange/?q=tag%3A%22courseware+module%22&sort=downloads_desc_30d). - -# Educator Resources -- [Educator Page](https://www.mathworks.com/academia/educators.html) - - -# Contribute - -Looking for more? Find an issue? Have a suggestion? Please contact the [MathWorks teaching resources team](mailto:%20onlineteaching@mathworks.com). If you want to contribute directly to this project, you can find information about how to do so in the [CONTRIBUTING.md](https://github.com/MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python/blob/release/CONTRIBUTING.md) page on GitHub. - -# Acknowledgements - -Many thanks to Blake Naccarato on his suggestions for improvement. - - - *©* Copyright 2024 The MathWorks™, Inc - - - + +# Programming: A Starter Project Using MATLAB with Python + +Learn how to call Python code, convert data types and units, and use App Designer to build an app to display your results. + +[![View on File Exchange](https://www.mathworks.com/matlabcentral/images/matlab-file-exchange.svg)](https://www.mathworks.com/matlabcentral/fileexchange/116490-programming-a-starter-project-using-matlab-and-python) or [![Open in MATLAB Online](https://www.mathworks.com/images/responsive/global/open-in-matlab-online.svg)](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python&project=MATLABwithPython.prj&file=README.mlx) + +[![MATLAB Versions Tested](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2FMathWorks-Teaching-Resources%2FProgramming-A-Starter-Project-Using-MATLAB-and-Python%2Frelease%2FImages%2FTestedWith.json)](https://MathWorks-Teaching-Resources.github.io/Programming-A-Starter-Project-Using-MATLAB-and-Python) + +**Curriculum Module** + +_Created with R2024a. Compatible with R2024a and later releases._ + +# Information + +This curriculum module contains interactive [MATLAB® live scripts](https://www.mathworks.com/products/matlab/live-editor.html) that teach fundamental concepts and basic terminology related to programming computers. + +# Prerequisite Domain Knowledge + +This module assumes familiarity with basic programming concepts such as variables, data types, and functions, structures including arrays and structs, and control flows including if/else as well as how to use them in MATLAB. These ideas are all presented with interactive examples in [Fundamentals of Programming](https://www.mathworks.com/matlabcentral/fileexchange/103225-fundamentals-of-programming), [Programming: Organizing Data](https://www.mathworks.com/matlabcentral/fileexchange/115900-programming-organizing-data), [Programming: Structuring Code](https://www.mathworks.com/matlabcentral/fileexchange/115905-programming-structuring-code). + + +## Background + +You can use these live scripts as demonstrations in lectures, class activities, or interactive assignments outside class. This module explores interactions between systems by using the OpenWeather API and calling into Python from MATLAB. **Programming: A Starter Project Using MATLAB with Python** covers using the Run Python Code Live Task to run provided Python code, accessing the OpenWeather API to check the weather, extracting useful data from the API call, and using App Designer to create an app with a personalized weather display. + + +The instructions inside the live scripts will guide you through the exercises and activities. Get started with each live script by running it one section at a time. To stop running the script or a section midway (for example, when an animation is in progress), use the EndIcon.png Stop button in the **RUN** section of the **Live Editor** tab in the MATLAB Toolstrip. + +## Contact Us + +Contact the [MathWorks Educator Content Development Team](mailto:onlineteaching@mathworks.com) if you would like to request assistance, provide feedback, or if you have a question. + + +## Prerequisites + +This module assumes familiarity with basic programming concepts such as variables, data types, and functions, structures including arrays and structs, and control flows including if/else as well as how to use them in MATLAB. These ideas are all presented with interactive examples in [Fundamentals of Programming](https://github.com/MathWorks-Teaching-Resources/Fundamentals-of-Programming), [Programming: Organizing Data](https://github.com/MathWorks-Teaching-Resources/Programming-Organizing-Data), [Programming: Structuring Code](https://github.com/MathWorks-Teaching-Resources/Programming-Structuring-Code). + + +## Getting Started +### Accessing the Module +### **On MATLAB Online:** + +Use the [OpenInMO.png](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python&project=MATLABwithPython.prj) link to download the module. You will be prompted to log in or create a MathWorks account. The project will be loaded, and you will see an app with several navigation options to get you started. + +### **On Desktop:** + +Download or clone this repository. Open MATLAB, navigate to the folder containing these scripts and double\-click on [MATLABwithPython.prj](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python&project=MATLABwithPython.prj&file=README.mlx). It will add the appropriate files to your MATLAB path and open an app that asks you where you would like to start. + + +Ensure you have all the required products (listed below) installed. If you need to include a product, add it using the Add\-On Explorer. To install an add\-on, go to the **Home** tab and select AddOnsIcon.png **Add-Ons** > **Get Add-Ons**. + + +## Products + +MATLAB® is used throughout. Tools from the Symbolic Math Toolbox™ are used to convert between different unit systems in the weather applications. + +# Scripts +## [**UsingMATLABwithPython.mlx**](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python&project=MATLABwithPython.prj&file=Scripts/UsingMATLABwithPython.mlx) +| | | +| :-- | :-- | +| **Introductory script**
| **In this script, students will...**
| +| RunPythonLiveTask.png
| $\bullet$ check that an appropriate version of Python is installed and visible within MATLAB
$\bullet$ explore the Run Python Code Live Task
$\bullet$ try running Python commands and a Python script from MATLAB
| +| | | + +## [**CheckingTheWeather.mlx**](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python&project=MATLABwithPython.prj&file=Scripts/CheckingTheWeather.mlx) +| | | +| :-- | :-- | +| **Investigatory script**
| **In this script, students will...**
| +| WeatherStruct.png
| $\bullet$ set up an account with OpenWeather to create your own API key
$\bullet$ use existing Python code to make an API call to OpenWeather
$\bullet$ use MATLAB to explore the data returned by the API call, including data type conversions and unit conversions
| +| | | + +## [**CreateCurrentWeatherApp.mlx**](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python&project=MATLABwithPython.prj&file=Scripts/CreateCurrentWeatherApp.mlx) +| | | +| :-- | :-- | +| **Application script**
| **In this script, students will...**
| +| WeatherDashboard.png
| $\bullet$ use App Designer to build a custom app using the code from CheckingTheWeather.mlx
$\bullet$ create callbacks, properties, and functions
$\bullet$ design and organize a UI with drag and drop elements
$\bullet$ reuse code from CheckingTheWeather in a new context
| +| | | + +# Apps + +Both of these apps require running Python from MATLAB, as set up in UsingMATLABwithPython and an OpenWeather API key, as set up in CheckingTheWeather. + +- **CurrentWeatherAppDemo.mlapp** shows the results of working through CreateCurrentWeatherApp. +- **WeatherDisplay.mlapp** shows one elaborated version of the basic current weather app. + +# License + +The license for this module is available in the [LICENSE.md](https://github.com/MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python/blob/release/LICENSE.md). + +# Related Courseware Modules +| | | | +| :-- | :-- | :-- | +| **Courseware Module**
| **Sample Content**
| **Available on:**
| +| [**Fundamentals of Programming**](https://www.mathworks.com/matlabcentral/fileexchange/103225-fundamentals-of-programming)
Learn the basics of how to make a computer
accept, store, and compute with information
| MouseThoughtBubble.png
| [OpenInFX.png](https://www.mathworks.com/matlabcentral/fileexchange/103225-fundamentals-of-programming)
[OpenInMO.png](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Fundamentals-of-Programming&project=FundamentalsofProgramming.prj)
[GitHub](https://github.com/MathWorks-Teaching-Resources/Fundamentals-of-Programming)
| +| [**Programming: Structuring Code**](https://www.mathworks.com/matlabcentral/fileexchange/115905-programming-structuring-code)
Learn how to organize your code into functions,
debug, comment, and share
| RGBTurkeys.png
| [OpenInFX.png](https://www.mathworks.com/matlabcentral/fileexchange/115905-programming-structuring-code)
[OpenInMO.png](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Programming-Structuring-Code&project=StructuringCode.prj)
[GitHub](https://github.com/MathWorks-Teaching-Resources/Programming-Structuring-Code)
| +| [**Programming: Organizing Data**](https://www.mathworks.com/matlabcentral/fileexchange/115900-programming-organizing-data)
Learn more about strings, numeric data types,
memory, and ways of storing data
| SeaSurfaceTemps.png
| [OpenInFX.png](https://www.mathworks.com/matlabcentral/fileexchange/115900-programming-organizing-data)
[OpenInMO.png](https://matlab.mathworks.com/open/github/v1?repo=MathWorks-Teaching-Resources/Programming-Organizing-Data&project=OrganizingData.prj)
[GitHub](https://github.com/MathWorks-Teaching-Resources/Programming-Organizing-Data)
| +| | | | + + +Or feel free to explore our other [modular courseware content](https://www.mathworks.com/matlabcentral/fileexchange/?q=author%3A%22MathWorks+Educator+Content+Development+Team%22&sort=relevancy). + +# Educator Resources +- [Educator Page](https://www.mathworks.com/academia/educators.html) + +# Contribute + +Looking for more? Find an issue? Have a suggestion? Please contact the [MathWorks Educator Content Development team](mailto:%20onlineteaching@mathworks.com). If you want to contribute directly to this project, you can find information about how to do so in the [CONTRIBUTING.md](https://github.com/MathWorks-Teaching-Resources/Programming-A-Starter-Project-Using-MATLAB-and-Python/blob/release/CONTRIBUTING.md) page on GitHub. + +# Acknowledgments + +Many thanks to Blake Naccarato on his suggestions for improvement. + + +*©* Copyright 2025 The MathWorks, Inc + + diff --git a/README.mlx b/README.mlx index aa6043b..63575f7 100644 Binary files a/README.mlx and b/README.mlx differ diff --git a/Scripts/CheckingTheWeather.mlx b/Scripts/CheckingTheWeather.mlx index a3b54c9..f6e122f 100644 Binary files a/Scripts/CheckingTheWeather.mlx and b/Scripts/CheckingTheWeather.mlx differ diff --git a/Scripts/CreateCurrentWeatherApp.mlx b/Scripts/CreateCurrentWeatherApp.mlx index bff8f18..8411010 100644 Binary files a/Scripts/CreateCurrentWeatherApp.mlx and b/Scripts/CreateCurrentWeatherApp.mlx differ diff --git a/Scripts/SampleWeatherDashboard.mlx b/Scripts/SampleWeatherDashboard.mlx deleted file mode 100644 index 1ddefd8..0000000 Binary files a/Scripts/SampleWeatherDashboard.mlx and /dev/null differ diff --git a/Scripts/UsingMATLABwithPython.mlx b/Scripts/UsingMATLABwithPython.mlx index 88e7002..9a5db69 100644 Binary files a/Scripts/UsingMATLABwithPython.mlx and b/Scripts/UsingMATLABwithPython.mlx differ diff --git a/Scripts/WeatherForecast.mlx b/Scripts/WeatherForecast.mlx deleted file mode 100644 index 75489ca..0000000 Binary files a/Scripts/WeatherForecast.mlx and /dev/null differ diff --git a/Scripts/checkweather.py b/Scripts/checkweather.py deleted file mode 100644 index 307fc43..0000000 --- a/Scripts/checkweather.py +++ /dev/null @@ -1,111 +0,0 @@ -# -*- coding: utf-8 -*- -''' -Python 3.8 code -Created on Thu Jan 4 20:41:45 2018 -Modified May 2022 - -@author: Heather Gorr -Updated by: Emma Smith Zbarsky -Copyright 2018-2022 The MathWorks, Inc. - -Create the Openweather API-structured URL using One Call -as described in the documentation -https://openweathermap.org/api/one-call-api -This API is licensed under CC BY-SA 4.0, as documented here: -https://creativecommons.org/licenses/by-sa/4.0/ -''' - -# checkweather.py -import datetime -import json -import urllib.request - -BASE_URL = 'http://api.openweathermap.org/data/3.0/onecall?lat={}&lon={}&exclude=minutely,daily,alerts&units={}&appid={}' - -def get_weather(lat,lon,apikey,**kwargs): - '''get current conditions in specified location, e.g., - lat = 42.2775 N, lon = 71.3468 W is Natick, MA, US - get_current_weather('42.2775','-71.3468',key,units='metric')''' - - # Initialize json_data - json_data = {'Feedback': 'nothing'} - - # Set a default of imperial units - info = {'units':'imperial'} - for key, value in kwargs.items(): - info[key] = value - - try: - url = BASE_URL.format(lat,lon,info['units'],apikey) - json_data = json.loads(urllib.request.urlopen(url).read()) - except urllib.error.URLError: - # if the One Call weather API doesn't work, return an error - print('We cannot access the weather service') - - return json_data - - -def parse_current_json(json_data): - '''parse and extract json data from the current weather data''' - # Initialize weather_info - weather_info = {'Feedback': 'nothing'} - - try: - # select data of interest from dictionary - weather_info = json_data["current"] - # add current date and time - weather_info['current_time'] = str(datetime.datetime.now()) - # make sure values are returned as floats - weather_info['temp'] = float(weather_info['temp']) - weather_info['feels_like'] = float(weather_info['feels_like']) - weather_info['pressure'] = float(weather_info['pressure']) - weather_info['humidity'] = float(weather_info['humidity']) - weather_info['wind_speed'] = float(weather_info['wind_speed']) - weather_info['wind_deg'] = float(weather_info['wind_deg']) - weather_info['uvi'] = float(weather_info['uvi']) - weather_info['clouds'] = float(weather_info['clouds']) - weather_info['timezone'] = json_data['timezone'] - - except KeyError as e: - print('Something went wrong while parsing current json') - raise e - - return weather_info - - -def parse_forecast(json_data): - '''parse and extract json data from the forecast weather data''' - - import array - - weather_info = {'Feedback': 'nothing'} - - try: - timezone = json_data['timezone'] - data = json_data['hourly'] - # create arrays - temp = [] - pressure = [] - humidity = [] - speed = [] - deg = [] - date = [] - - # loop over all and add to arrays - for i in range(48): - x1 = data[i] - temp.append(x1['temp']) - pressure.append(x1['pressure']) - humidity.append(x1['humidity']) - speed.append(x1['wind_speed']) - deg.append(x1['wind_deg']) - date.append(x1['dt']) - - # create dictionary - weather_info = dict(current_time=date,temp=temp,deg=deg, - speed=speed,humidity=humidity,pressure=pressure,timezone=timezone) - except KeyError as e: - print('Something went wrong with parsing the forecast json.') - raise e - - return weather_info diff --git a/SoftwareTests/CheckTestResults.m b/SoftwareTests/CheckTestResults.m index fef0728..579ad43 100644 --- a/SoftwareTests/CheckTestResults.m +++ b/SoftwareTests/CheckTestResults.m @@ -4,7 +4,7 @@ end properties (ClassSetupParameter) - Project = {''}; + Project = {currentProject()}; end properties (TestParameter) @@ -15,8 +15,8 @@ methods (TestParameterDefinition,Static) function Version = GetResults(Project) - RootFolder = currentProject().RootFolder; - Version = dir(fullfile(RootFolder,"SoftwareTests","TestResults*.txt")); + RootFolder = Project.RootFolder; + Version = dir(fullfile(RootFolder,"public","TestResults*.txt")); Version = extractBetween([Version.name],"TestResults_",".txt"); end @@ -37,9 +37,11 @@ function SetUpSmokeTest(testCase,Project) methods(Test) function CheckResults(testCase,Version) - File = fullfile("SoftwareTests","TestResults_"+Version+".txt"); + File = fullfile("public","TestResults_"+Version+".txt"); Results = readtable(File,TextType="string"); - testCase.verifyTrue(all(Results.Passed)); + if ~all(Results.Passed) + error("Some of the tests did not pass.") + end end end diff --git a/SoftwareTests/FunctionTests.m b/SoftwareTests/FunctionTests.m index cfcd234..c490eb2 100644 --- a/SoftwareTests/FunctionTests.m +++ b/SoftwareTests/FunctionTests.m @@ -1,7 +1,14 @@ classdef FunctionTests < matlab.unittest.TestCase + % https://www.mathworks.com/help/matlab/matlab_prog/use-parameters-in-class-based-tests.html + methods(Test) + function IsKnownPythonVersion(testCase) + Known = CheckPythonVersion; + verifyTrue(testCase,Known) + end + end % methods end % classdef \ No newline at end of file diff --git a/SoftwareTests/OrigSmokeTests.m b/SoftwareTests/OrigSmokeTests.m new file mode 100644 index 0000000..e9a1427 --- /dev/null +++ b/SoftwareTests/OrigSmokeTests.m @@ -0,0 +1,149 @@ +classdef SmokeTests < matlab.unittest.TestCase + + properties (ClassSetupParameter) + Project = {''}; + end + + properties (TestParameter) + Scripts; + end + + methods (TestParameterDefinition,Static) + + function Scripts = GetScriptName(Project) + RootFolder = currentProject().RootFolder; + Scripts = dir(fullfile(RootFolder,"Scripts","*.mlx")); + Scripts = {Scripts.name}; + end + + end + + methods (TestClassSetup) + + function SetUpSmokeTest(testCase,Project) + try + currentProject; + catch ME + warning("Project is not loaded.") + end + end + + + end + + + + methods(Test) + + function SmokeRun(testCase,Scripts) + Filename = string(Scripts); + switch (Filename) + case "CreateCurrentWeatherApp.mlx" + disp("Skipping " + Filename) + case "CheckingTheWeather.mlx" + txt = readlines("Response.json"); + pycode = ["import checkcurrentweather" + "import json" + "" + "json_data = json.loads(txt)" + "currentWeather = checkcurrentweather.parse_current_json(json_data)" + ]; + APISmokeTest(testCase,Filename,pycode,txt) + case "WeatherForecast.mlx" + % txt = readlines("ResponseOneCall.json"); + % pycode = ["import checkweather" + % "" + % "json_data = json.loads(txt)" + % "currentWeather = checkweather.parse_current_json(json_data)" + % "forecastWeather = checkweather.parse_forecast_json(json_data)" + % ]; + % APISmokeTest(testCase,Filename,pycode,txt) + case "SampleWeatherDashboard.mlx" + + otherwise + SimpleSmokeTest(testCase,Filename) + end + end + + end + + + methods (Access = private) + + function APISmokeTest(testCase,Filename,pycode,txt) + RootFolder = currentProject().RootFolder; + cd(RootFolder) + cd Scripts + if Filename == "CheckingTheWeather.mlx" + [currentWeather] = pyrun(pycode, "currentWeather","txt",txt); + elseif Filename == "WeatherForecast.mlx" + [currentWeather,forecastWeather] = pyrun(pycode,["currentWeather","forecastWeather"],"txt",txt); + end + + disp(">> Running " + Filename); + try + run(fullfile(Filename)); + catch ME + testCase.verifyTrue(false,ME.message); + end + + try + % Log the opened figures to the test reports + Figures = findall(groot,'Type','figure'); + Figures = flipud(Figures); + if ~isempty(Figures) + for f = 1:size(Figures,1) + FigDiag = matlab.unittest.diagnostics.FigureDiagnostic(Figures(f)); + log(testCase,1,FigDiag); + end + end + catch ME + disp("Failed to capture images") + end + close all + end + + function RunMyFile(~,Filename) + run(Filename); + end + + function SimpleSmokeTest(testCase,Filename) + + % Run the Smoke test + RootFolder = currentProject().RootFolder; + cd(RootFolder) + disp(">> Running " + Filename); + try + RunMyFile(testCase,Filename) + catch ME + testCase.verifyTrue(false,ME.message); + end + + % Log the opened figures to the test reports + try + Figures = findall(groot,'Type','figure'); + Figures = flipud(Figures); + if ~isempty(Figures) + for f = 1:size(Figures,1) + FigDiag = matlab.unittest.diagnostics.FigureDiagnostic(Figures(f)); + log(testCase,1,FigDiag); + end + end + catch ME + disp("Failed to capture figures") + end + close all + + end + + end + + methods (TestClassTeardown) + + function closeAllFigure(testCase) + close all force % Close figure windows + end + + end % methods (TestClassTeardown) + +end \ No newline at end of file diff --git a/SoftwareTests/PostFiles/PostCheckingTheWeather.m b/SoftwareTests/PostFiles/PostCheckingTheWeather.m new file mode 100644 index 0000000..5e1183b --- /dev/null +++ b/SoftwareTests/PostFiles/PostCheckingTheWeather.m @@ -0,0 +1,3 @@ +% Post-run script for CheckingTheWeather.mlx +% ---- Post-run commands ----- + diff --git a/SoftwareTests/PostFiles/PostCheckingTheWeatherSoln.m b/SoftwareTests/PostFiles/PostCheckingTheWeatherSoln.m new file mode 100644 index 0000000..3bca581 --- /dev/null +++ b/SoftwareTests/PostFiles/PostCheckingTheWeatherSoln.m @@ -0,0 +1,7 @@ +% Post-run script for CheckingTheWeatherSoln.mlx +% ---- Post-run commands ----- + +writelines("",fullfile(currentProject().RootFolder,"Scripts","myAPIkey.txt")) +if exist(fullfile(currentProject().RootFolder,"InstructorResources","Solutions","Response.json"),"file") + delete(fullfile(currentProject().RootFolder,"InstructorResources","Solutions","Response.json")) +end \ No newline at end of file diff --git a/SoftwareTests/PostFiles/PostCreateCurrentWeatherApp.m b/SoftwareTests/PostFiles/PostCreateCurrentWeatherApp.m new file mode 100644 index 0000000..9530d14 --- /dev/null +++ b/SoftwareTests/PostFiles/PostCreateCurrentWeatherApp.m @@ -0,0 +1,3 @@ +% Post-run script for CreateCurrentWeatherApp.mlx +% ---- Post-run commands ----- + diff --git a/SoftwareTests/PostFiles/PostCreateCurrentWeatherAppSoln.m b/SoftwareTests/PostFiles/PostCreateCurrentWeatherAppSoln.m new file mode 100644 index 0000000..197fb02 --- /dev/null +++ b/SoftwareTests/PostFiles/PostCreateCurrentWeatherAppSoln.m @@ -0,0 +1,4 @@ +% Post-run script for CreateCurrentWeatherAppSoln.mlx +% ---- Post-run commands ----- + +delete(findall(groot,'Name','MATLAB App')) \ No newline at end of file diff --git a/SoftwareTests/PostFiles/PostUsingMATLABwithPython.m b/SoftwareTests/PostFiles/PostUsingMATLABwithPython.m new file mode 100644 index 0000000..9416232 --- /dev/null +++ b/SoftwareTests/PostFiles/PostUsingMATLABwithPython.m @@ -0,0 +1,3 @@ +% Post-run script for UsingMATLABwithPython.mlx +% ---- Post-run commands ----- + diff --git a/SoftwareTests/PostFiles/PostUsingMATLABwithPythonSoln.m b/SoftwareTests/PostFiles/PostUsingMATLABwithPythonSoln.m new file mode 100644 index 0000000..e6e41ac --- /dev/null +++ b/SoftwareTests/PostFiles/PostUsingMATLABwithPythonSoln.m @@ -0,0 +1,3 @@ +% Post-run script for UsingMATLABwithPythonSoln.mlx +% ---- Post-run commands ----- + diff --git a/SoftwareTests/PostSmokeTest.m b/SoftwareTests/PostSmokeTest.m new file mode 100644 index 0000000..080cd0b --- /dev/null +++ b/SoftwareTests/PostSmokeTest.m @@ -0,0 +1,66 @@ +function PostSmokeTest(ShowReport) +arguments + ShowReport (1,1) logical = false; +end + +import matlab.unittest.plugins.TestRunnerPlugin; + +% Create the runner: +Runner = matlab.unittest.TestRunner.withTextOutput; + +% Create report folder: +Folder = fullfile(currentProject().RootFolder,"public"); +if ~isfolder(Folder) + mkdir(Folder) +end + +% Add HTML plugin: +Plugin = matlab.unittest.plugins.TestReportPlugin.producingHTML(Folder,... + "IncludingPassingDiagnostics",true,... + "IncludingCommandWindowText",false,... + "LoggingLevel",matlab.automation.Verbosity(1)); +Runner.addPlugin(Plugin); + + +% Create Test Suite +Suite = testsuite("CheckTestResults"); + +% Run the test suite +Results = Runner.run(Suite); + + +% Format the results in a table and save them +Results = table(Results'); +Version = extractBetween(string(Results.Name),"Version=",")"); +Passed = logical(Results.Passed); + +% Add link to other report +File = fileread(fullfile("public","index.html")); +for iVer = 1:length(Version) + File = replace(File,"Version="+Version(iVer),... + sprintf('%s',Version(iVer),"Version="+Version(iVer))); +end +writelines(File,fullfile("public","index.html"),"WriteMode","overwrite"); + +% Format the JSON file +Badge = struct; +Badge.schemaVersion = 1; +Badge.label = "Test Status"; +if all(Passed) + Badge.color = "success"; + Badge.message = join("R"+Version," | "); +elseif any(Passed) + Badge.color = "yellowgreen"; + Badge.message = join("R"+Version(Passed)," | "); +elseif all(~Passed) + Badge.color = "critical"; + Badge.message = join("R"+Version," | "); +end +Badge = jsonencode(Badge); +writelines(Badge,fullfile("Images","TestedWith.json")); + +if ShowReport + web(fullfile(Folder,"index.html")) +end + +end \ No newline at end of file diff --git a/SoftwareTests/PreFiles/PreCheckingTheWeather.m b/SoftwareTests/PreFiles/PreCheckingTheWeather.m new file mode 100644 index 0000000..13788f4 --- /dev/null +++ b/SoftwareTests/PreFiles/PreCheckingTheWeather.m @@ -0,0 +1,17 @@ +% Pre-run script for CheckingTheWeather.mlx +% ---- Known Issues ----- +KnownIssuesID = ""; +% ---- Pre-run commands ----- + +txt = readlines("Response.json"); +curpath = pwd; +mypath = fullfile(currentProject().RootFolder,"Scripts"); +cd(mypath) +pycode = ["import checkcurrentweather" + "import json" + "" + "json_data = json.loads(txt)" + "currentWeather = checkcurrentweather.parse_current_json(json_data)" + ]; +[currentWeather] = pyrun(pycode, "currentWeather","txt",txt); +cd(curpath) \ No newline at end of file diff --git a/SoftwareTests/PreFiles/PreCheckingTheWeatherSoln.m b/SoftwareTests/PreFiles/PreCheckingTheWeatherSoln.m new file mode 100644 index 0000000..fdff0a6 --- /dev/null +++ b/SoftwareTests/PreFiles/PreCheckingTheWeatherSoln.m @@ -0,0 +1,23 @@ +% Pre-run script for CheckingTheWeatherSoln.mlx +% ---- Known Issues ----- +KnownIssuesID = ""; +% ---- Pre-run commands ----- + +try +copyfile(fullfile(currentProject().RootFolder,"InternalFiles","Solutions","myAPIkey.txt"),... + fullfile(currentProject().RootFolder,"Scripts","myAPIkey.txt")) +catch + copyfile("Response.json",fullfile(currentProject().RootFolder,"InstructorResources","Solutions","Response.json")) + pycode = ["import checkcurrentweather" + "import json" + "" + "json_data = json.load(open(""Response.json""))" + "currentWeather = checkcurrentweather.parse_current_json(json_data)" + ]; + apikey = "TestString"; + pyrun = @(txt,out,varargin)TestPyRun(pycode,out,varargin); +end + +function out = TestPyRun(txt,in,varargin) +out = pyrun(txt,in); +end diff --git a/SoftwareTests/PreFiles/PreCreateCurrentWeatherApp.m b/SoftwareTests/PreFiles/PreCreateCurrentWeatherApp.m new file mode 100644 index 0000000..ca7aa9b --- /dev/null +++ b/SoftwareTests/PreFiles/PreCreateCurrentWeatherApp.m @@ -0,0 +1,6 @@ +% Pre-run script for CreateCurrentWeatherApp.mlx +% ---- Known Issues ----- +KnownIssuesID = ""; +% ---- Pre-run commands ----- + +appdesigner = @()disp("Open App Designer here."); \ No newline at end of file diff --git a/SoftwareTests/PreFiles/PreCreateCurrentWeatherAppSoln.m b/SoftwareTests/PreFiles/PreCreateCurrentWeatherAppSoln.m new file mode 100644 index 0000000..ece9e8e --- /dev/null +++ b/SoftwareTests/PreFiles/PreCreateCurrentWeatherAppSoln.m @@ -0,0 +1,7 @@ +% Pre-run script for CreateCurrentWeatherAppSoln.mlx +% ---- Known Issues ----- +KnownIssuesID = ""; +% ---- Pre-run commands ----- + +open = @(x)run(x); + diff --git a/SoftwareTests/PreFiles/PreUsingMATLABwithPython.m b/SoftwareTests/PreFiles/PreUsingMATLABwithPython.m new file mode 100644 index 0000000..7a4b22b --- /dev/null +++ b/SoftwareTests/PreFiles/PreUsingMATLABwithPython.m @@ -0,0 +1,5 @@ +% Pre-run script for UsingMATLABwithPython.mlx +% ---- Known Issues ----- +KnownIssuesID = ""; +% ---- Pre-run commands ----- + diff --git a/SoftwareTests/PreFiles/PreUsingMATLABwithPythonSoln.m b/SoftwareTests/PreFiles/PreUsingMATLABwithPythonSoln.m new file mode 100644 index 0000000..d8e1896 --- /dev/null +++ b/SoftwareTests/PreFiles/PreUsingMATLABwithPythonSoln.m @@ -0,0 +1,5 @@ +% Pre-run script for UsingMATLABwithPythonSoln.mlx +% ---- Known Issues ----- +KnownIssuesID = ""; +% ---- Pre-run commands ----- + diff --git a/SoftwareTests/PreFiles/Response.json b/SoftwareTests/PreFiles/Response.json new file mode 100644 index 0000000..d8135a4 --- /dev/null +++ b/SoftwareTests/PreFiles/Response.json @@ -0,0 +1 @@ +{"coord":{"lon":-71.3468,"lat":42.2775},"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"base":"stations","main":{"temp":274.86,"feels_like":270.03,"temp_min":273.44,"temp_max":276.25,"pressure":1003,"humidity":76},"visibility":10000,"wind":{"speed":5.66,"deg":280,"gust":10.8},"clouds":{"all":75},"dt":1710986897,"sys":{"type":2,"id":2005486,"country":"US","sunrise":1710931672,"sunset":1710975450},"timezone":-14400,"id":4944994,"name":"Natick","cod":200} \ No newline at end of file diff --git a/SoftwareTests/PreFiles/ResponseOneCall.json b/SoftwareTests/PreFiles/ResponseOneCall.json new file mode 100644 index 0000000..676ca0b --- /dev/null +++ b/SoftwareTests/PreFiles/ResponseOneCall.json @@ -0,0 +1,114 @@ +{ + "lat":33.44, + "lon":-94.04, + "timezone":"America/Chicago", + "timezone_offset":-18000, + "current":{ + "dt":1684929490, + "sunrise":1684926645, + "sunset":1684977332, + "temp":292.55, + "feels_like":292.87, + "pressure":1014, + "humidity":89, + "dew_point":290.69, + "uvi":0.16, + "clouds":53, + "visibility":10000, + "wind_speed":3.13, + "wind_deg":93, + "wind_gust":6.71, + "weather":[ + { + "id":803, + "main":"Clouds", + "description":"broken clouds", + "icon":"04d" + } + ] + }, + "minutely":[ + { + "dt":1684929540, + "precipitation":0 + } + ], + "hourly":[ + { + "dt":1684926000, + "temp":292.01, + "feels_like":292.33, + "pressure":1014, + "humidity":91, + "dew_point":290.51, + "uvi":0, + "clouds":54, + "visibility":10000, + "wind_speed":2.58, + "wind_deg":86, + "wind_gust":5.88, + "weather":[ + { + "id":803, + "main":"Clouds", + "description":"broken clouds", + "icon":"04n" + } + ], + "pop":0.15 + } + ], + "daily":[ + { + "dt":1684951200, + "sunrise":1684926645, + "sunset":1684977332, + "moonrise":1684941060, + "moonset":1684905480, + "moon_phase":0.16, + "summary":"Expect a day of partly cloudy with rain", + "temp":{ + "day":299.03, + "min":290.69, + "max":300.35, + "night":291.45, + "eve":297.51, + "morn":292.55 + }, + "feels_like":{ + "day":299.21, + "night":291.37, + "eve":297.86, + "morn":292.87 + }, + "pressure":1016, + "humidity":59, + "dew_point":290.48, + "wind_speed":3.98, + "wind_deg":76, + "wind_gust":8.92, + "weather":[ + { + "id":500, + "main":"Rain", + "description":"light rain", + "icon":"10d" + } + ], + "clouds":92, + "pop":0.47, + "rain":0.15, + "uvi":9.23 + } + ], + "alerts": [ + { + "sender_name": "NWS Philadelphia - Mount Holly (New Jersey, Delaware, Southeastern Pennsylvania)", + "event": "Small Craft Advisory", + "start": 1684952747, + "end": 1684988747, + "description": "...SMALL CRAFT ADVISORY REMAINS IN EFFECT FROM 5 PM THIS\nAFTERNOON TO 3 AM EST FRIDAY...\n* WHAT...North winds 15 to 20 kt with gusts up to 25 kt and seas\n3 to 5 ft expected.\n* WHERE...Coastal waters from Little Egg Inlet to Great Egg\nInlet NJ out 20 nm, Coastal waters from Great Egg Inlet to\nCape May NJ out 20 nm and Coastal waters from Manasquan Inlet\nto Little Egg Inlet NJ out 20 nm.\n* WHEN...From 5 PM this afternoon to 3 AM EST Friday.\n* IMPACTS...Conditions will be hazardous to small craft.", + "tags": [] + } + ] +} \ No newline at end of file diff --git a/SoftwareTests/ResponseOneCallOneLine.json b/SoftwareTests/ResponseOneCallOneLine.json new file mode 100644 index 0000000..63c5ef7 --- /dev/null +++ b/SoftwareTests/ResponseOneCallOneLine.json @@ -0,0 +1 @@ +{"lat":33.44,"lon":-94.04,"timezone":"America/Chicago","timezone_offset":-18000,"current":{"dt":1684929490,"sunrise":1684926645,"sunset":1684977332,"temp":292.55,"feels_like":292.87,"pressure":1014,"humidity":89,"dew_point":290.69,"uvi":0.16,"clouds":53,"visibility":10000,"wind_speed":3.13,"wind_deg":93,"wind_gust":6.71,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}]},"minutely":[{"dt":1684929540,"precipitation":0}],"hourly":[{"dt":1684926000,"temp":292.01,"feels_like":292.33,"pressure":1014,"humidity":91,"dew_point":290.51,"uvi":0,"clouds":54,"visibility":10000,"wind_speed":2.58,"wind_deg":86,"wind_gust":5.88,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"pop":0.15}],"daily":[{"dt":1684951200,"sunrise":1684926645,"sunset":1684977332,"moonrise":1684941060,"moonset":1684905480,"moon_phase":0.16,"summary":"Expect a day of partly cloudy with rain","temp":{"day":299.03,"min":290.69,"max":300.35,"night":291.45,"eve":297.51,"morn":292.55},"feels_like":{"day":299.21,"night":291.37,"eve":297.86,"morn":292.87},"pressure":1016,"humidity":59,"dew_point":290.48,"wind_speed":3.98,"wind_deg":76,"wind_gust":8.92,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"clouds":92,"pop":0.47,"rain":0.15,"uvi":9.23} ],"alerts": [{"sender_name": "NWS Philadelphia - Mount Holly (New Jersey, Delaware, Southeastern Pennsylvania)","event": "Small Craft Advisory","start": 1684952747,"end": 1684988747,"description": "...SMALL CRAFT ADVISORY REMAINS IN EFFECT FROM 5 PM THIS\nAFTERNOON TO 3 AM EST FRIDAY...\n* WHAT...North winds 15 to 20 kt with gusts up to 25 kt and seas\n3 to 5 ft expected.\n* WHERE...Coastal waters from Little Egg Inlet to Great Egg\nInlet NJ out 20 nm, Coastal waters from Great Egg Inlet to\nCape May NJ out 20 nm and Coastal waters from Manasquan Inlet\nto Little Egg Inlet NJ out 20 nm.\n* WHEN...From 5 PM this afternoon to 3 AM EST Friday.\n* IMPACTS...Conditions will be hazardous to small craft.","tags": [ ]}] } \ No newline at end of file diff --git a/SoftwareTests/RunAllTests.m b/SoftwareTests/RunAllTests.m index 87675d1..046a893 100644 --- a/SoftwareTests/RunAllTests.m +++ b/SoftwareTests/RunAllTests.m @@ -1,42 +1,42 @@ -function RunAllTest(EnableReport,ReportFolder) +function RunAllTests(ShowReport) arguments - EnableReport (1,1) logical = false; - ReportFolder (1,1) string = "public"; + ShowReport (1,1) logical = false; end import matlab.unittest.plugins.TestReportPlugin; % Create a runner Runner = matlab.unittest.TestRunner.withTextOutput; -if EnableReport - Folder = fullfile(currentProject().RootFolder,ReportFolder); - if ~isfolder(Folder) - mkdir(Folder) - else - rmdir(Folder,'s') - mkdir(Folder) - end - Plugin = TestReportPlugin.producingHTML(Folder,... - "IncludingPassingDiagnostics",true,... - "IncludingCommandWindowText",true,... - "LoggingLevel",matlab.automation.Verbosity(1)); - Runner.addPlugin(Plugin); +Folder = fullfile(currentProject().RootFolder,"public",version("-release")); +if ~isfolder(Folder) + mkdir(Folder) +else + rmdir(Folder,'s') + mkdir(Folder) end +Plugin = TestReportPlugin.producingHTML(Folder,... + "IncludingPassingDiagnostics",true,... + "IncludingCommandWindowText",true,... + "LoggingLevel",matlab.automation.Verbosity(1)); +Runner.addPlugin(Plugin); + % Create the test suite with SmokeTest and Function test if they exist Suite = testsuite("SmokeTests"); Suite = [Suite testsuite("FunctionTests")]; +Suite = [Suite testsuite("SolnSmokeTests")]; % Run the test suite Results = Runner.run(Suite); -if EnableReport +if ShowReport web(fullfile(Folder,"index.html")) end % Format the results in a table and save them ResultsTable = table(Results') -writetable(ResultsTable,fullfile("SoftwareTests","TestResults_R"+version("-release")+".txt")); +writetable(ResultsTable,fullfile(currentProject().RootFolder,... + "public","TestResults_"+version("-release")+".txt")); % Assert success of test assertSuccess(Results); diff --git a/SoftwareTests/SmokeTests.m b/SoftwareTests/SmokeTests.m index e9a1427..dac5c42 100644 --- a/SoftwareTests/SmokeTests.m +++ b/SoftwareTests/SmokeTests.m @@ -1,149 +1,152 @@ classdef SmokeTests < matlab.unittest.TestCase + properties + RootFolder + sparedEditors % Files already open when the test starts + end % properties + properties (ClassSetupParameter) - Project = {''}; - end + Project = {currentProject()}; + end % ClassSetupParameter properties (TestParameter) - Scripts; - end + File; + end % TestParameter methods (TestParameterDefinition,Static) - function Scripts = GetScriptName(Project) + function File = RetrieveFile(Project) %#ok + % Retrieve student template files: RootFolder = currentProject().RootFolder; - Scripts = dir(fullfile(RootFolder,"Scripts","*.mlx")); - Scripts = {Scripts.name}; + File = dir(fullfile(RootFolder,"Scripts","*.m")); + File = [File; dir(fullfile(RootFolder,"Scripts","*.mlx"))]; + File = {File.name}; end - end + end % Static TestParameterDefinition methods (TestClassSetup) - function SetUpSmokeTest(testCase,Project) - try - currentProject; - catch ME - warning("Project is not loaded.") - end - end - - - end - + function SetUpSmokeTest(testCase,Project) %#ok + % Navigate to project root folder: + testCase.RootFolder = Project.RootFolder; + cd(testCase.RootFolder) + + % Close the StartUp app if still open: + delete(findall(groot,'Name','StartUp App')) + % Log MATLAB version: + testCase.log("Running in " + version) + end - methods(Test) + end % TestClassSetup - function SmokeRun(testCase,Scripts) - Filename = string(Scripts); - switch (Filename) - case "CreateCurrentWeatherApp.mlx" - disp("Skipping " + Filename) - case "CheckingTheWeather.mlx" - txt = readlines("Response.json"); - pycode = ["import checkcurrentweather" - "import json" - "" - "json_data = json.loads(txt)" - "currentWeather = checkcurrentweather.parse_current_json(json_data)" - ]; - APISmokeTest(testCase,Filename,pycode,txt) - case "WeatherForecast.mlx" - % txt = readlines("ResponseOneCall.json"); - % pycode = ["import checkweather" - % "" - % "json_data = json.loads(txt)" - % "currentWeather = checkweather.parse_current_json(json_data)" - % "forecastWeather = checkweather.parse_forecast_json(json_data)" - % ]; - % APISmokeTest(testCase,Filename,pycode,txt) - case "SampleWeatherDashboard.mlx" - - otherwise - SimpleSmokeTest(testCase,Filename) + methods(TestMethodSetup) + function recordEditorsToSpare(testCase) + testCase.sparedEditors = matlab.desktop.editor.getAll; + testCase.sparedEditors = {testCase.sparedEditors.Filename}; + end + end % TestMethodSetup + + methods(TestMethodTeardown) + function closeOpenedEditors_thenDeleteWorkingDir(testCase) + openEditors = matlab.desktop.editor.getAll; + for editor=openEditors(1:end) + if any(strcmp(editor.Filename, testCase.sparedEditors)) + continue; + end + % if not on our list, close the file + editor.close(); end end + end % TestMethodTeardown - end + methods(Test) + function SmokeRun(testCase,File) - methods (Access = private) + % Navigate to project root folder: + cd(testCase.RootFolder) + FileToRun = string(File); - function APISmokeTest(testCase,Filename,pycode,txt) - RootFolder = currentProject().RootFolder; - cd(RootFolder) - cd Scripts - if Filename == "CheckingTheWeather.mlx" - [currentWeather] = pyrun(pycode, "currentWeather","txt",txt); - elseif Filename == "WeatherForecast.mlx" - [currentWeather,forecastWeather] = pyrun(pycode,["currentWeather","forecastWeather"],"txt",txt); - end + % Pre-test: + PreFiles = CheckPreFile(testCase,FileToRun); + run(PreFiles); - disp(">> Running " + Filename); + % Run SmokeTest + disp(">> Running " + FileToRun); try - run(fullfile(Filename)); - catch ME - testCase.verifyTrue(false,ME.message); + run(fullfile("Scripts",FileToRun)); + catch ME + end - try - % Log the opened figures to the test reports + % Post-test: + PostFiles = CheckPostFile(testCase,FileToRun); + run(PostFiles) + + % Log every figure created during run: Figures = findall(groot,'Type','figure'); Figures = flipud(Figures); if ~isempty(Figures) for f = 1:size(Figures,1) - FigDiag = matlab.unittest.diagnostics.FigureDiagnostic(Figures(f)); - log(testCase,1,FigDiag); + if ~isempty(Figures(f).Number) + FigDiag = matlab.unittest.diagnostics.FigureDiagnostic(Figures(f),'Formats','png'); + log(testCase,1,FigDiag); + end end end - catch ME - disp("Failed to capture images") - end - close all - end - function RunMyFile(~,Filename) - run(Filename); - end - - function SimpleSmokeTest(testCase,Filename) - - % Run the Smoke test - RootFolder = currentProject().RootFolder; - cd(RootFolder) - disp(">> Running " + Filename); - try - RunMyFile(testCase,Filename) - catch ME - testCase.verifyTrue(false,ME.message); + % Close all figures and Simulink models + close all force + if any(matlab.addons.installedAddons().Name == "Simulink") + bdclose all end - % Log the opened figures to the test reports - try - Figures = findall(groot,'Type','figure'); - Figures = flipud(Figures); - if ~isempty(Figures) - for f = 1:size(Figures,1) - FigDiag = matlab.unittest.diagnostics.FigureDiagnostic(Figures(f)); - log(testCase,1,FigDiag); + % Rethrow error if any + if exist("ME","var") + if ~any(strcmp(ME.identifier,KnownIssuesID)) + rethrow(ME) end end - catch ME - disp("Failed to capture figures") - end - close all end + + end % Test Methods - end - methods (TestClassTeardown) + methods (Access = private) - function closeAllFigure(testCase) - close all force % Close figure windows + function Path = CheckPreFile(testCase,Filename) + PreFile = "Pre"+extractBefore(Filename,".m")+".m"; + PreFilePath = fullfile(testCase.RootFolder,"SoftwareTests","PreFiles",PreFile); + if ~isfolder(fullfile(testCase.RootFolder,"SoftwareTests/PreFiles")) + mkdir(fullfile(testCase.RootFolder,"SoftwareTests/PreFiles")) + end + if ~isfile(PreFilePath) + writelines("% Pre-run script for "+Filename,PreFilePath) + writelines("% ---- Known Issues -----",PreFilePath,'WriteMode','append'); + writelines("KnownIssuesID = "+char(34)+char(34)+";",PreFilePath,'WriteMode','append'); + writelines("% ---- Pre-run commands -----",PreFilePath,'WriteMode','append'); + writelines(" ",PreFilePath,'WriteMode','append'); + end + Path = PreFilePath; + end + + function Path = CheckPostFile(testCase,Filename) + PostFile = "Post"+extractBefore(Filename,".m")+".m"; + PostFilePath = fullfile(testCase.RootFolder,"SoftwareTests","PostFiles",PostFile); + if ~isfolder(fullfile(testCase.RootFolder,"SoftwareTests/PostFiles")) + mkdir(fullfile(testCase.RootFolder,"SoftwareTests/PostFiles")) + end + if ~isfile(PostFilePath) + writelines("% Post-run script for "+Filename,PostFilePath) + writelines("% ---- Post-run commands -----",PostFilePath,'WriteMode','append'); + writelines(" ",PostFilePath,'WriteMode','append'); + end + Path = PostFilePath; end - end % methods (TestClassTeardown) + end % Private Methods -end \ No newline at end of file +end % Smoketests \ No newline at end of file diff --git a/SoftwareTests/SolnSmokeTests.m b/SoftwareTests/SolnSmokeTests.m new file mode 100644 index 0000000..7caeb21 --- /dev/null +++ b/SoftwareTests/SolnSmokeTests.m @@ -0,0 +1,177 @@ +classdef SolnSmokeTests < matlab.unittest.TestCase + + properties + RootFolder + isSolnOnPath + sparedEditors % Track open files + end % properties + + properties (ClassSetupParameter) + Project = {currentProject()}; + end % ClassSetupParameter + + methods(TestMethodSetup) + function recordEditorsToSpare(testCase) + testCase.sparedEditors = matlab.desktop.editor.getAll; + testCase.sparedEditors = {testCase.sparedEditors.Filename}; + end + end % TestMethodSetup + + methods(TestMethodTeardown) + function closeOpenedEditors_thenDeleteWorkingDir(testCase) + openEditors = matlab.desktop.editor.getAll; + for editor=openEditors(1:end) + if any(strcmp(editor.Filename, testCase.sparedEditors)) + continue; + end + % if not on our list, close the file + editor.close(); + end + end + end % TestMethodTeardown + + properties (TestParameter) + File; + end % TestParameter + + methods (TestParameterDefinition,Static) + + function File = GetScriptName(Project) + % Retrieve student template files: + RootFolder = Project.RootFolder; + File = dir(fullfile(RootFolder,"Scripts","*.m")); + File = [File; dir(fullfile(RootFolder,"Scripts","*.mlx"))]; + File = {File.name}; + end + + end % Static TestParameterDefinition + + methods (TestClassSetup) + + function SetUpPath(testCase,Project) + % Navigate to project root folder: + testCase.RootFolder = Project.RootFolder; + cd(testCase.RootFolder) + + % Check that solutions are on path: + testCase.isSolnOnPath = isfolder("Solutions"); + if testCase.isSolnOnPath == 0 + addpath(genpath(fullfile(testCase.RootFolder,"InstructorResources","Solutions"))) + end + + % Close the StartUp app if still open: + delete(findall(groot,'Name','StartUp App')) + + % Log MATLAB version: + testCase.log("Running in " + version) + + end % function setUpPath + + end % methods (TestClassSetup) + + methods(Test) + + % Check that solutions files exist for each of the student + % templates + function ExistSolns(testCase,File) + SolutionName = replace(string(File),".m","Soln.m"); + assert(exist(SolutionName,"file"),"Missing solutions for "+File); + end + + + function SmokeRun(testCase,File) + + % Navigate to project root folder: + cd(testCase.RootFolder) + FileToRun = replace(string(File),".m","Soln.m"); + + % Pre-test: + PreFiles = CheckPreFile(testCase,FileToRun); + run(PreFiles); + + % Run SmokeTest + disp(">> Running " + FileToRun); + try + run(fullfile("InstructorResources","Solutions",FileToRun)); + catch ME + + end + + % Post-test: + PostFiles = CheckPostFile(testCase,FileToRun); + run(PostFiles) + + % Log every figure created during run: + Figures = findall(groot,'Type','figure'); + Figures = flipud(Figures); + if ~isempty(Figures) + for f = 1:size(Figures,1) + if ~isempty(Figures(f).Number) + FigDiag = matlab.unittest.diagnostics.FigureDiagnostic(Figures(f),'Formats','png'); + log(testCase,1,FigDiag); + end + end + end + + % Close all figures and Simulink models + close all force + if any(matlab.addons.installedAddons().Name == "Simulink") + bdclose all + end + + % Rethrow error if any + if exist("ME","var") + if ~any(strcmp(ME.identifier,KnownIssuesID)) + rethrow(ME) + end + end + + end + + end % Test Methods + + methods (Access = private) + + function Path = CheckPreFile(testCase,Filename) + PreFile = "Pre"+extractBefore(Filename,".m")+".m"; + PreFilePath = fullfile(testCase.RootFolder,"SoftwareTests","PreFiles",PreFile); + if ~isfolder(fullfile(testCase.RootFolder,"SoftwareTests/PreFiles")) + mkdir(fullfile(testCase.RootFolder,"SoftwareTests/PreFiles")) + end + if ~isfile(PreFilePath) + writelines("% Pre-run script for "+Filename,PreFilePath) + writelines("% ---- Known Issues -----",PreFilePath,'WriteMode','append'); + writelines("KnownIssuesID = "+char(34)+char(34)+";",PreFilePath,'WriteMode','append'); + writelines("% ---- Pre-run commands -----",PreFilePath,'WriteMode','append'); + writelines(" ",PreFilePath,'WriteMode','append'); + end + Path = PreFilePath; + end + + function Path = CheckPostFile(testCase,Filename) + PostFile = "Post"+extractBefore(Filename,".m")+".m"; + PostFilePath = fullfile(testCase.RootFolder,"SoftwareTests","PostFiles",PostFile); + if ~isfolder(fullfile(testCase.RootFolder,"SoftwareTests/PostFiles")) + mkdir(fullfile(testCase.RootFolder,"SoftwareTests/PostFiles")) + end + if ~isfile(PostFilePath) + writelines("% Post-run script for "+Filename,PostFilePath) + writelines("% ---- Post-run commands -----",PostFilePath,'WriteMode','append'); + writelines(" ",PostFilePath,'WriteMode','append'); + end + Path = PostFilePath; + end + + end % Private Access Methods + + methods (TestClassTeardown) + + function ResetPath(testCase) + if ~testCase.isSolnOnPath && exist("Solutions","dir") + rmpath(genpath(fullfile(currentProject().RootFolder,"InstructorResources","Solutions"))) + end + end + + end % TestClassTeardown + +end % SolnSmokeTests diff --git a/SoftwareTests/TestResults_R2023a.txt b/SoftwareTests/TestResults_R2023a.txt new file mode 100644 index 0000000..5c9f3b0 --- /dev/null +++ b/SoftwareTests/TestResults_R2023a.txt @@ -0,0 +1,6 @@ +Name,Passed,Failed,Incomplete,Duration,Details +SmokeTests[Project=0x0_char]/SmokeRun(Scripts=CheckingTheWeather.mlx),1,0,0,8.2960691, +SmokeTests[Project=0x0_char]/SmokeRun(Scripts=CreateCurrentWeatherApp.mlx),1,0,0,0.0004901, +SmokeTests[Project=0x0_char]/SmokeRun(Scripts=SampleWeatherDashboard.mlx),1,0,0,0.0004588, +SmokeTests[Project=0x0_char]/SmokeRun(Scripts=UsingMATLABwithPython.mlx),1,0,0,2.2924885, +SmokeTests[Project=0x0_char]/SmokeRun(Scripts=WeatherForecast.mlx),1,0,0,0.3437829, diff --git a/SoftwareTests/TestResults_R2024a.txt b/SoftwareTests/TestResults_R2024a.txt new file mode 100644 index 0000000..73f4a8d --- /dev/null +++ b/SoftwareTests/TestResults_R2024a.txt @@ -0,0 +1,6 @@ +Name,Passed,Failed,Incomplete,Duration,Details +SmokeTests[Project=0x0_char]/SmokeRun(Scripts=CheckingTheWeather.mlx),1,0,0,0.1316994, +SmokeTests[Project=0x0_char]/SmokeRun(Scripts=CreateCurrentWeatherApp.mlx),1,0,0,0.0003877, +SmokeTests[Project=0x0_char]/SmokeRun(Scripts=SampleWeatherDashboard.mlx),1,0,0,0.000197, +SmokeTests[Project=0x0_char]/SmokeRun(Scripts=UsingMATLABwithPython.mlx),1,0,0,0.3888675, +SmokeTests[Project=0x0_char]/SmokeRun(Scripts=WeatherForecast.mlx),1,0,0,0.0044386, diff --git a/Utilities/OldVersions/CheckingTheWeatherOld.mlx b/Utilities/OldVersions/CheckingTheWeatherOld.mlx deleted file mode 100644 index 735122b..0000000 Binary files a/Utilities/OldVersions/CheckingTheWeatherOld.mlx and /dev/null differ diff --git a/Utilities/OldVersions/UsingMATLABwithPythonOld.mlx b/Utilities/OldVersions/UsingMATLABwithPythonOld.mlx deleted file mode 100644 index 22483ab..0000000 Binary files a/Utilities/OldVersions/UsingMATLABwithPythonOld.mlx and /dev/null differ diff --git a/Utilities/ProjectShutdown.m b/Utilities/ProjectShutdown.m index 20b6121..8c3c77c 100644 --- a/Utilities/ProjectShutdown.m +++ b/Utilities/ProjectShutdown.m @@ -1,62 +1,2 @@ -% function ProjectShutdown -% % Reset module to original state that is expected when loading in a new -% % MATLAB version. -% proj = currentProject; -% -% MoveFilesAround("R2023b","MainMenu.mlx",proj,Flag="Close") -% MoveFilesAround("R2023b","README.mlx",proj,Flag="Close") -% % MoveFilesAround("R2024a","UsingMATLABwithPython.mlx",proj,Flag="Close") -% % MoveFilesAround("R2024a","CheckingTheWeather.mlx",proj,Flag="Close") -% -% end -% -% function MoveFilesAround(ReleaseString,FileString,proj,opts) -% arguments -% ReleaseString (1,1) string {mustBeRelease} -% FileString (1,1) string {mustLookLikeFile} -% proj -% opts.OldStr (1,1) string = "Old" -% opts.NewStr (1,1) string = "New" -% opts.Flag (1,1) string {mustBeMember(opts.Flag,["Open" "Close"])} = "Open" -% end -% -% if ~contains(ReleaseString,"R") -% ReleaseString = "R"+ReleaseString; -% end -% [FileLoc,FileName,FileExt] = fileparts(which(FileString)); -% -% if isMATLABReleaseOlderThan(ReleaseString) -% FileStringOld = FileName+opts.OldStr+FileExt; -% FileStringNew = FileName+opts.NewStr+FileExt; -% FileString = string(FileName)+FileExt; -% try -% if opts.Flag == "Open" -% if exist(fullfile(proj.RootFolder,"Utilities","OldVersions",FileStringOld),"file") -% movefile(fullfile(FileLoc,FileString), fullfile("Utilities","OldVersions",FileStringNew)) -% movefile(fullfile("Utilities","OldVersions",FileStringOld),fullfile(FileLoc,FileString)) -% end -% else -% if exist(fullfile(proj.RootFolder,"Utilities","OldVersions",FileStringNew),"file") -% movefile(fullfile(FileLoc,FileString), fullfile("Utilities","OldVersions",FileStringOld)) -% movefile(fullfile("Utilities","OldVersions",FileStringNew),fullfile(FileLoc,FileString)) -% end -% end -% catch -% disp("Failed to move " + FileString + ".") -% end -% end -% end -% -% function mustBeRelease(str) -% if ~contains(str,"R") -% str = "R"+str; -% end -% pattern = "R"+digitsPattern(4)+("a"|"b"); -% assert(matches(str,pattern),"ReleaseString must be a valid MATLAB release.") -% end -% -% function mustLookLikeFile(str) -% [~,f,e] = fileparts(str); -% assert(~isempty(f)) -% assert(e==".mlx"|e==".m"|e==".slx") -% end \ No newline at end of file +% Close the StartUp app if still open: +delete(findall(groot,'Name','StartUp App')) \ No newline at end of file diff --git a/Utilities/ProjectStartup.m b/Utilities/ProjectStartup.m deleted file mode 100644 index 605a6b5..0000000 --- a/Utilities/ProjectStartup.m +++ /dev/null @@ -1,76 +0,0 @@ -function ProjectStartup -% Set up check for version number -proj = currentProject; - -% Address changes in the MATLAB language and capabilities -MoveFilesAround("R2023b","MainMenu.mlx",proj) -MoveFilesAround("R2023b","README.mlx",proj) -MoveFilesAround("R2024a","UsingMATLABwithPython.mlx",proj) -MoveFilesAround("R2024a","CheckingTheWeather.mlx",proj) - -% Offer navigation aids and feedback opportunities -ProjectStartupApp -end - -function MoveFilesAround(ReleaseString,FileString,proj,opts) -arguments - ReleaseString (1,1) string {mustBeRelease} - FileString (1,1) string {mustLookLikeFile} - proj - opts.OldStr (1,1) string = "Old" - opts.NewStr (1,1) string = "New" - opts.Flag (1,1) string {mustBeMember(opts.Flag,["Open" "Close"])} = "Open" -end - -if ~contains(ReleaseString,"R") - ReleaseString = "R"+ReleaseString; -end -[FileLoc,FileName,FileExt] = fileparts(which(FileString)); - -FileStringOld = FileName+opts.OldStr+FileExt; -FileStringNew = FileName+opts.NewStr+FileExt; -FileString = string(FileName)+FileExt; - -if isMATLABReleaseOlderThan(ReleaseString) - try - if opts.Flag == "Open" - if exist(fullfile(proj.RootFolder,"Utilities","OldVersions",FileStringOld),"file") - movefile(fullfile(FileLoc,FileString), fullfile("Utilities","OldVersions",FileStringNew)) - movefile(fullfile("Utilities","OldVersions",FileStringOld),fullfile(FileLoc,FileString)) - end - % else - % if exist(fullfile(proj.RootFolder,"Utilities","OldVersions",FileStringNew),"file") - % movefile(fullfile(FileLoc,FileString), fullfile("Utilities","OldVersions",FileStringOld)) - % movefile(fullfile("Utilities","OldVersions",FileStringNew),fullfile(FileLoc,FileString)) - % end - end - catch - disp("Failed to move " + FileString + ".") - end -else - try - if opts.Flag == "Open" - if exist(fullfile(proj.RootFolder,"Utilities","OldVersions",FileStringNew),"file") - movefile(fullfile(FileLoc,FileString), fullfile("Utilities","OldVersions",FileStringOld)) - movefile(fullfile("Utilities","OldVersions",FileStringNew),fullfile(FileLoc,FileString)) - end - end - catch - disp("Failed to move " + FileString + ".") - end -end -end - -function mustBeRelease(str) -if ~contains(str,"R") - str = "R"+str; -end -pattern = "R"+digitsPattern(4)+("a"|"b"); -assert(matches(str,pattern),"ReleaseString must be a valid MATLAB release.") -end - -function mustLookLikeFile(str) -[~,f,e] = fileparts(str); -assert(~isempty(f)) -assert(e==".mlx"|e==".m"|e==".slx") -end \ No newline at end of file diff --git a/Utilities/ProjectStartupApp.m b/Utilities/ProjectStartupApp.m index bd94512..0cb0ebb 100644 --- a/Utilities/ProjectStartupApp.m +++ b/Utilities/ProjectStartupApp.m @@ -2,85 +2,37 @@ % Properties that correspond to app components properties (Access = public) - UIFigure matlab.ui.Figure - TabGroup matlab.ui.container.TabGroup - WelcomeTab matlab.ui.container.Tab - Image matlab.ui.control.Image - READMEButton matlab.ui.control.Button - ReviewUsButton matlab.ui.control.Button - MainMenuButton matlab.ui.control.Button - WelcomeTitle matlab.ui.control.Label - TabReview matlab.ui.container.Tab - OtherButton matlab.ui.control.Button - StudentButton matlab.ui.control.Button - FacultyButton matlab.ui.control.Button - Q1 matlab.ui.control.Label - ReviewTitle matlab.ui.control.Label - ReviewText matlab.ui.control.Label + StartUpAppUIFigure matlab.ui.Figure + FeedBackPanel matlab.ui.container.Panel + FeedBackGrid matlab.ui.container.GridLayout + ReviewTitle matlab.ui.control.Label + ReviewText matlab.ui.control.Label + OtherButton matlab.ui.control.Button + StudentButton matlab.ui.control.Button + FacultyButton matlab.ui.control.Button + Q1 matlab.ui.control.Label + WelcomePanel matlab.ui.container.Panel + WelcomeGrid matlab.ui.container.GridLayout + WelcomeTitle matlab.ui.control.Label + CoverImage matlab.ui.control.Image + ReviewUsButton matlab.ui.control.Button + READMEButton matlab.ui.control.Button + MainMenuButton matlab.ui.control.Button end - + % Properties to be modified properties (Access = private) GitHubOrganization = "MathWorks-Teaching-Resources"; % Description GitHubRepository = "Programming-A-Starter-Project-Using-MATLAB-and-Python"; + ImagePath {mustBeFile} = fullfile("Images","windTokyo.gif"); end -%% How to customize the app? -%{ - - This StartUp app is designed to be customized to your module. It - requires a minimum number of customization: - - 1. Change "Module Template" in app.WelcomeTitle by your module name - 2. Change "Module Template" in app.ReviewTitle by your module name - 3. Change the GitHubRepository (line 25) to the correct value - 4. Change image in app.Image by the cover image you would like for your - module. This image should be located in rootFolder/Images - 5. Create your MS Form: - a. Make a copy of the Faculty and the Student Template surveys - b. Customize the name of the survey to match the name of your - survey - c. Click on "Collect responses", select "Anyone can respond" and - copy the form link to SetupAppLinks (see step 6). - 5. Create your MS Sway: - a. Go to MS Sway - b. Create a blank sway - c. Add the name of your module to the title box - d. Click "Share", Select "Anyone with a link", Select "View" - e. Copy the sway link to SetupAppLinks (see step 6). - 6. Add the Survey and Sway link to Utilities/SurveyLinks using - SetupAppLinks.mlx in InternalFiles/RequiredFunctions/StartUpFcn - 7. Save > Export to .m file and save the result as - Utilities/ProjectStartupApp.m - -%} - - methods (Access = private, Static) - function pingSway(app) - try - if ~ispref("MCCTEAM") - load Utilities\SurveyLinks.mat SwayLink - webread(SwayLink); - end - catch - end - end - - function openStudentForm(app) - try - load Utilities\SurveyLinks.mat StudentFormLink - web(StudentFormLink); - catch - end - end + properties (Access = private) + InitPosition; + ProjectName; + end - function openFacultyForm(app) - try - load Utilities\SurveyLinks.mat FacultyFormLink - web(FacultyFormLink); - catch - end - end + methods (Access = private, Static) function saveSettings(isReviewed,numLoad) try @@ -98,8 +50,11 @@ function saveSettings(isReviewed,numLoad) % Code that executes after component creation function startupFcn(app) - % Move gui to center of screen - movegui(app.UIFigure,"center") + % Copy title and set cover image + app.ProjectName = currentProject().Name; + app.WelcomeTitle.Text = app.ProjectName; + app.ReviewTitle.Text = app.WelcomeTitle.Text; + app.CoverImage.ImageSource = app.ImagePath; % Switch tab to review if has not been reviewed yet if isfile(fullfile("Utilities","ProjectSettings.mat")) @@ -110,36 +65,22 @@ function startupFcn(app) numLoad = 1; % Initialize counter end - % Switch tab for review + % Select tab to display if ~isReviewed && numLoad > 2 isReviewed = true; - app.TabGroup.SelectedTab = app.TabReview; + app.FeedBackGrid.Parent = app.StartUpAppUIFigure; + else + app.WelcomeGrid.Parent = app.StartUpAppUIFigure; end + app.InitPosition = app.StartUpAppUIFigure.Position; % Save new settings app.saveSettings(isReviewed,numLoad) - - % Download links to survey (should only work when module goes - % public on GitHub) - try - import matlab.net.* - import matlab.net.http.* - - Request = RequestMessage; - Request.Method = 'GET'; - Address = URI("http://api.github.com/repos/"+app.GitHubOrganization+... - "/"+app.GitHubRepository+"/contents/Utilities/SurveyLinks.mat"); - Request.Header = HeaderField("X-GitHub-Api-Version","2022-11-28"); - Request.Header(2) = HeaderField("Accept","application/vnd.github+json"); - [Answer,~,~] = send(Request,Address); - websave(fullfile("Utilities/SurveyLinks.mat"),Answer.Body.Data.download_url); - catch - end - + end - % Close request function: UIFigure - function UIFigureCloseRequest(app, event) + % Close request function: StartUpAppUIFigure + function StartUpAppUIFigureCloseRequest(app, event) if event.Source == app.READMEButton open README.mlx elseif event.Source == app.MainMenuButton @@ -158,38 +99,63 @@ function UIFigureCloseRequest(app, event) % Button pushed function: MainMenuButton function MainMenuButtonPushed(app, event) - UIFigureCloseRequest(app,event) + StartUpAppUIFigureCloseRequest(app,event) end % Button pushed function: FacultyButton function FacultyButtonPushed(app, event) - app.pingSway; - app.openFacultyForm; - UIFigureCloseRequest(app,event) + % Open Faculty Form + import matlab.net.* + % Create the URI object with the base URL + uri = URI('https://forms.office.com/Pages/ResponsePage.aspx','literal'); + % Set the Query property with an array of QueryParameter objects + uri.Query = [ + QueryParameter('id', 'ETrdmUhDaESb3eUHKx3B5mlcO9AKxC5AgMAKBg6OKuBUNTVXVlBTS0lOU0hPRExYMldGWldVQUhIRC4u') + QueryParameter('r2017080ed20546d1a2db18fe36421929', app.ProjectName) + ]; + web(strrep(uri.EncodedURI,"+","%20")) + StartUpAppUIFigureCloseRequest(app,event) end % Button pushed function: StudentButton function StudentButtonPushed(app, event) - app.pingSway; - app.openStudentForm; - UIFigureCloseRequest(app,event) + % Open Student Form + import matlab.net.* + % Create the URI object with the base URL + uri = URI('https://forms.office.com/Pages/ResponsePage.aspx','literal'); + % Set the Query property with an array of QueryParameter objects + uri.Query = [ + QueryParameter('id', 'ETrdmUhDaESb3eUHKx3B5mlcO9AKxC5AgMAKBg6OKuBUNlNBOVRZSDZHT1VTMzA4MjdHSUdVR0o3Vy4u') + QueryParameter('r362e367caa234debbf4f65a58a0338e6', app.ProjectName) + ]; + web(strrep(uri.EncodedURI,"+","%20")) + StartUpAppUIFigureCloseRequest(app,event) end % Button pushed function: OtherButton function OtherButtonPushed(app, event) - app.pingSway; - app.openStudentForm; - UIFigureCloseRequest(app,event) + % Open Student Form + import matlab.net.* + % Create the URI object with the base URL + uri = URI('https://forms.office.com/Pages/ResponsePage.aspx','literal'); + % Set the Query property with an array of QueryParameter objects + uri.Query = [ + QueryParameter('id', 'ETrdmUhDaESb3eUHKx3B5mlcO9AKxC5AgMAKBg6OKuBUNlNBOVRZSDZHT1VTMzA4MjdHSUdVR0o3Vy4u') + QueryParameter('r362e367caa234debbf4f65a58a0338e6', app.ProjectName) + ]; + web(strrep(uri.EncodedURI,"+","%20")) + StartUpAppUIFigureCloseRequest(app,event) end % Button pushed function: ReviewUsButton function ReviewUsButtonPushed(app, event) - app.TabGroup.SelectedTab = app.TabReview; + app.WelcomeGrid.Parent = app.WelcomePanel; + app.FeedBackGrid.Parent = app.StartUpAppUIFigure; end % Button pushed function: READMEButton function READMEButtonPushed(app, event) - UIFigureCloseRequest(app,event) + StartUpAppUIFigureCloseRequest(app,event) end end @@ -199,134 +165,130 @@ function READMEButtonPushed(app, event) % Create UIFigure and components function createComponents(app) - % Create UIFigure and hide until all components are created - app.UIFigure = uifigure('Visible', 'off'); - app.UIFigure.AutoResizeChildren = 'off'; - app.UIFigure.Position = [100 100 276 430]; - app.UIFigure.Name = 'MATLAB App'; - app.UIFigure.Resize = 'off'; - app.UIFigure.CloseRequestFcn = createCallbackFcn(app, @UIFigureCloseRequest, true); + % Create StartUpAppUIFigure and hide until all components are created + app.StartUpAppUIFigure = uifigure('Visible', 'off'); + app.StartUpAppUIFigure.AutoResizeChildren = 'off'; + app.StartUpAppUIFigure.Position = [100 100 276 430]; + app.StartUpAppUIFigure.Name = 'StartUp App'; + app.StartUpAppUIFigure.CloseRequestFcn = createCallbackFcn(app, @StartUpAppUIFigureCloseRequest, true); - % Create TabGroup - app.TabGroup = uitabgroup(app.UIFigure); - app.TabGroup.AutoResizeChildren = 'off'; - app.TabGroup.Position = [1 1 276 460]; + % Create WelcomePanel + app.WelcomePanel = uipanel(app.StartUpAppUIFigure); + app.WelcomePanel.AutoResizeChildren = 'off'; + app.WelcomePanel.Position = [-551 33 244 410]; - % Create WelcomeTab - app.WelcomeTab = uitab(app.TabGroup); - app.WelcomeTab.AutoResizeChildren = 'off'; - app.WelcomeTab.Title = 'Tab'; - - % Create WelcomeTitle - app.WelcomeTitle = uilabel(app.WelcomeTab); - app.WelcomeTitle.HorizontalAlignment = 'center'; - app.WelcomeTitle.VerticalAlignment = 'top'; - app.WelcomeTitle.WordWrap = 'on'; - app.WelcomeTitle.FontSize = 20; - app.WelcomeTitle.FontWeight = 'bold'; - app.WelcomeTitle.FontColor = [0.14 0.14 0.14]; - app.WelcomeTitle.Position = [30 345 219 74]; - app.WelcomeTitle.Text = ' Programming: A Starter Project Using MATLAB with Python'; + % Create WelcomeGrid + app.WelcomeGrid = uigridlayout(app.WelcomePanel); + app.WelcomeGrid.ColumnWidth = {'1x', '8x', '1x'}; + app.WelcomeGrid.RowHeight = {'2x', '5x', '1x', '1x', '1x'}; % Create MainMenuButton - app.MainMenuButton = uibutton(app.WelcomeTab, 'push'); + app.MainMenuButton = uibutton(app.WelcomeGrid, 'push'); app.MainMenuButton.ButtonPushedFcn = createCallbackFcn(app, @MainMenuButtonPushed, true); - app.MainMenuButton.BackgroundColor = [0.129411764705882 0.129411764705882 0.129411764705882]; app.MainMenuButton.FontSize = 18; - app.MainMenuButton.FontColor = [0.850980392156863 0.850980392156863 0.850980392156863]; - app.MainMenuButton.Position = [59 96 161 35]; + app.MainMenuButton.Layout.Row = 3; + app.MainMenuButton.Layout.Column = 2; app.MainMenuButton.Text = 'Main Menu'; - % Create ReviewUsButton - app.ReviewUsButton = uibutton(app.WelcomeTab, 'push'); - app.ReviewUsButton.ButtonPushedFcn = createCallbackFcn(app, @ReviewUsButtonPushed, true); - app.ReviewUsButton.BackgroundColor = [0.129411764705882 0.129411764705882 0.129411764705882]; - app.ReviewUsButton.FontSize = 18; - app.ReviewUsButton.FontColor = [0.850980392156863 0.850980392156863 0.850980392156863]; - app.ReviewUsButton.Position = [59 10 161 35]; - app.ReviewUsButton.Text = 'Review Us'; - % Create READMEButton - app.READMEButton = uibutton(app.WelcomeTab, 'push'); + app.READMEButton = uibutton(app.WelcomeGrid, 'push'); app.READMEButton.ButtonPushedFcn = createCallbackFcn(app, @READMEButtonPushed, true); - app.READMEButton.BackgroundColor = [0.129411764705882 0.129411764705882 0.129411764705882]; app.READMEButton.FontSize = 18; - app.READMEButton.FontColor = [0.850980392156863 0.850980392156863 0.850980392156863]; - app.READMEButton.Position = [59 53 161 35]; + app.READMEButton.Layout.Row = 4; + app.READMEButton.Layout.Column = 2; app.READMEButton.Text = 'README'; - % Create Image - app.Image = uiimage(app.WelcomeTab); - app.Image.Position = [16 141 245 209]; - app.Image.ImageSource = 'windTokyo.gif'; + % Create ReviewUsButton + app.ReviewUsButton = uibutton(app.WelcomeGrid, 'push'); + app.ReviewUsButton.ButtonPushedFcn = createCallbackFcn(app, @ReviewUsButtonPushed, true); + app.ReviewUsButton.FontSize = 18; + app.ReviewUsButton.Layout.Row = 5; + app.ReviewUsButton.Layout.Column = 2; + app.ReviewUsButton.Text = 'Review Us'; - % Create TabReview - app.TabReview = uitab(app.TabGroup); - app.TabReview.AutoResizeChildren = 'off'; - app.TabReview.Title = 'Tab2'; - app.TabReview.HandleVisibility = 'off'; + % Create CoverImage + app.CoverImage = uiimage(app.WelcomeGrid); + app.CoverImage.Layout.Row = 2; + app.CoverImage.Layout.Column = [1 3]; + app.CoverImage.ImageSource = 'windTokyo.gif'; - % Create ReviewText - app.ReviewText = uilabel(app.TabReview); - app.ReviewText.HorizontalAlignment = 'center'; - app.ReviewText.VerticalAlignment = 'top'; - app.ReviewText.WordWrap = 'on'; - app.ReviewText.FontSize = 18; - app.ReviewText.FontColor = [0.14 0.14 0.14]; - app.ReviewText.Position = [16 243 245 69]; - app.ReviewText.Text = 'Plese help us improve your experience by answering a few questions.'; + % Create WelcomeTitle + app.WelcomeTitle = uilabel(app.WelcomeGrid); + app.WelcomeTitle.HorizontalAlignment = 'center'; + app.WelcomeTitle.VerticalAlignment = 'top'; + app.WelcomeTitle.WordWrap = 'on'; + app.WelcomeTitle.FontSize = 24; + app.WelcomeTitle.FontWeight = 'bold'; + app.WelcomeTitle.Layout.Row = 1; + app.WelcomeTitle.Layout.Column = [1 3]; + app.WelcomeTitle.Text = ''; - % Create ReviewTitle - app.ReviewTitle = uilabel(app.TabReview); - app.ReviewTitle.HorizontalAlignment = 'center'; - app.ReviewTitle.VerticalAlignment = 'top'; - app.ReviewTitle.WordWrap = 'on'; - app.ReviewTitle.FontSize = 20; - app.ReviewTitle.FontWeight = 'bold'; - app.ReviewTitle.FontColor = [0.14 0.14 0.14]; - app.ReviewTitle.Position = [2 326 274 93]; - app.ReviewTitle.Text = ' Programming: A Starter Project Using MATLAB with Python'; + % Create FeedBackPanel + app.FeedBackPanel = uipanel(app.StartUpAppUIFigure); + app.FeedBackPanel.AutoResizeChildren = 'off'; + app.FeedBackPanel.Position = [-291 33 236 409]; + + % Create FeedBackGrid + app.FeedBackGrid = uigridlayout(app.FeedBackPanel); + app.FeedBackGrid.ColumnWidth = {'1x', '8x', '1x'}; + app.FeedBackGrid.RowHeight = {'2x', '3x', '2x', '1x', '1x', '1x'}; % Create Q1 - app.Q1 = uilabel(app.TabReview); + app.Q1 = uilabel(app.FeedBackGrid); app.Q1.HorizontalAlignment = 'center'; - app.Q1.VerticalAlignment = 'top'; app.Q1.WordWrap = 'on'; app.Q1.FontSize = 18; app.Q1.FontWeight = 'bold'; - app.Q1.FontColor = [0.14 0.14 0.14]; - app.Q1.Position = [16 141 245 69]; - app.Q1.Text = 'What describe you best?'; + app.Q1.Layout.Row = 3; + app.Q1.Layout.Column = [1 3]; + app.Q1.Text = 'What describes you best?'; % Create FacultyButton - app.FacultyButton = uibutton(app.TabReview, 'push'); + app.FacultyButton = uibutton(app.FeedBackGrid, 'push'); app.FacultyButton.ButtonPushedFcn = createCallbackFcn(app, @FacultyButtonPushed, true); - app.FacultyButton.BackgroundColor = [0.129411764705882 0.129411764705882 0.129411764705882]; app.FacultyButton.FontSize = 18; - app.FacultyButton.FontColor = [0.850980392156863 0.850980392156863 0.850980392156863]; - app.FacultyButton.Position = [64 127 150 40]; + app.FacultyButton.Layout.Row = 4; + app.FacultyButton.Layout.Column = 2; app.FacultyButton.Text = 'Faculty'; % Create StudentButton - app.StudentButton = uibutton(app.TabReview, 'push'); + app.StudentButton = uibutton(app.FeedBackGrid, 'push'); app.StudentButton.ButtonPushedFcn = createCallbackFcn(app, @StudentButtonPushed, true); - app.StudentButton.BackgroundColor = [0.129411764705882 0.129411764705882 0.129411764705882]; app.StudentButton.FontSize = 18; - app.StudentButton.FontColor = [0.850980392156863 0.850980392156863 0.850980392156863]; - app.StudentButton.Position = [64 80 150 40]; + app.StudentButton.Layout.Row = 5; + app.StudentButton.Layout.Column = 2; app.StudentButton.Text = 'Student'; % Create OtherButton - app.OtherButton = uibutton(app.TabReview, 'push'); + app.OtherButton = uibutton(app.FeedBackGrid, 'push'); app.OtherButton.ButtonPushedFcn = createCallbackFcn(app, @OtherButtonPushed, true); - app.OtherButton.BackgroundColor = [0.129411764705882 0.129411764705882 0.129411764705882]; app.OtherButton.FontSize = 18; - app.OtherButton.FontColor = [0.850980392156863 0.850980392156863 0.850980392156863]; - app.OtherButton.Position = [64 34 150 40]; + app.OtherButton.Layout.Row = 6; + app.OtherButton.Layout.Column = 2; app.OtherButton.Text = 'Other'; + % Create ReviewText + app.ReviewText = uilabel(app.FeedBackGrid); + app.ReviewText.HorizontalAlignment = 'center'; + app.ReviewText.WordWrap = 'on'; + app.ReviewText.FontSize = 14; + app.ReviewText.Layout.Row = 2; + app.ReviewText.Layout.Column = [1 3]; + app.ReviewText.Text = 'Please help us improve your experience by answering a few questions.'; + + % Create ReviewTitle + app.ReviewTitle = uilabel(app.FeedBackGrid); + app.ReviewTitle.HorizontalAlignment = 'center'; + app.ReviewTitle.VerticalAlignment = 'top'; + app.ReviewTitle.WordWrap = 'on'; + app.ReviewTitle.FontSize = 24; + app.ReviewTitle.FontWeight = 'bold'; + app.ReviewTitle.Layout.Row = 1; + app.ReviewTitle.Layout.Column = [1 3]; + app.ReviewTitle.Text = ''; + % Show the figure after all components are created - app.UIFigure.Visible = 'on'; + app.StartUpAppUIFigure.Visible = 'on'; end end @@ -340,7 +302,7 @@ function createComponents(app) createComponents(app) % Register the app with App Designer - registerApp(app, app.UIFigure) + registerApp(app, app.StartUpAppUIFigure) % Execute the startup function runStartupFcn(app, @startupFcn) @@ -354,7 +316,7 @@ function createComponents(app) function delete(app) % Delete UIFigure when app is deleted - delete(app.UIFigure) + delete(app.StartUpAppUIFigure) end end end \ No newline at end of file diff --git a/Utilities/SurveyLinks.mat b/Utilities/SurveyLinks.mat deleted file mode 100644 index 0b1802a..0000000 Binary files a/Utilities/SurveyLinks.mat and /dev/null differ diff --git a/resources/project/ZdVxxv9BsNz7MGUxtEc6Pq3qh1M/LTFuMOAnbbe8rFATn9tsEWBYh1Qd.xml b/resources/project/2zjcQkVJSJ_AwC9M8R9BTSESRzc/5R7G30JHZVF_lDxt6auPkBjLcVUd.xml similarity index 72% rename from resources/project/ZdVxxv9BsNz7MGUxtEc6Pq3qh1M/LTFuMOAnbbe8rFATn9tsEWBYh1Qd.xml rename to resources/project/2zjcQkVJSJ_AwC9M8R9BTSESRzc/5R7G30JHZVF_lDxt6auPkBjLcVUd.xml index 7a6326b..99772b4 100644 --- a/resources/project/ZdVxxv9BsNz7MGUxtEc6Pq3qh1M/LTFuMOAnbbe8rFATn9tsEWBYh1Qd.xml +++ b/resources/project/2zjcQkVJSJ_AwC9M8R9BTSESRzc/5R7G30JHZVF_lDxt6auPkBjLcVUd.xml @@ -1,4 +1,4 @@ - +