diff --git a/docs/framework/images/tutorial/release_notes/release_gen_1.png b/docs/framework/images/tutorial/release_notes/release_gen_1.png new file mode 100644 index 000000000..46d014b02 Binary files /dev/null and b/docs/framework/images/tutorial/release_notes/release_gen_1.png differ diff --git a/docs/framework/images/tutorial/release_notes/release_gen_2.png b/docs/framework/images/tutorial/release_notes/release_gen_2.png new file mode 100644 index 000000000..37d4c1a55 Binary files /dev/null and b/docs/framework/images/tutorial/release_notes/release_gen_2.png differ diff --git a/docs/framework/images/tutorial/release_notes/release_gen_3.png b/docs/framework/images/tutorial/release_notes/release_gen_3.png new file mode 100644 index 000000000..405d53d3f Binary files /dev/null and b/docs/framework/images/tutorial/release_notes/release_gen_3.png differ diff --git a/docs/framework/images/tutorial/release_notes/release_gen_4.png b/docs/framework/images/tutorial/release_notes/release_gen_4.png new file mode 100644 index 000000000..3a197bb9b Binary files /dev/null and b/docs/framework/images/tutorial/release_notes/release_gen_4.png differ diff --git a/docs/framework/images/tutorial/release_notes/release_gen_5.png b/docs/framework/images/tutorial/release_notes/release_gen_5.png new file mode 100644 index 000000000..8a4491c45 Binary files /dev/null and b/docs/framework/images/tutorial/release_notes/release_gen_5.png differ diff --git a/docs/framework/images/tutorial/release_notes/release_gen_6.png b/docs/framework/images/tutorial/release_notes/release_gen_6.png new file mode 100644 index 000000000..6ad41d380 Binary files /dev/null and b/docs/framework/images/tutorial/release_notes/release_gen_6.png differ diff --git a/docs/framework/release-notes-generator.mdx b/docs/framework/release-notes-generator.mdx new file mode 100644 index 000000000..2a74d46f8 --- /dev/null +++ b/docs/framework/release-notes-generator.mdx @@ -0,0 +1,412 @@ +--- +title: "Release notes generator" +--- +In this tutorial, you'll build a release notes generator using the Writer Framework. This application will help you generate release notes as formatted HTML for software updates based on user-provided data as a CSV file. You can check out the [finished code on GitHub](https://github.com/writer/framework-tutorials/release-notes-generator/end) to see what you'll be building. + +The application consists of two main parts: the backend, which processes the uploaded CSV file and generates the release notes, and the frontend, which displays the release notes and allows users to download them. + +![Finished release notes generator application](/framework/images/tutorial/release_notes/release_gen_1.png) + +## Setting up your project +### Creating a Writer app and getting your API key + +From the Home screen, click on **Build an app**. + +![Writer home screen](/framework/images/tutorial/release_notes/release_gen_2.png) + +Select Framework as the app type you want to create, enabling you to generate keys and build your app with the Writer Framework. + +![App type selection](/framework/images/tutorial/release_notes/release_gen_3.png) + +On the next screen, you can edit your Writer application name at the top left. Underneath "Authenticate with an API key," click "Reveal" to view and copy your API key. + + +### Creating the application + +Next, open your terminal and navigate to the directory where you want to create your application directory. + + + + To pass your API key to the Writer Framework, you'll need to set an environment variable called `WRITER_API_KEY`. Here's how you can set this variable in your terminal session: + + + ```sh On macOS/Linux + export WRITER_API_KEY=[key] + ``` + + ```sh On Windows + set WRITER_API_KEY=[key] + ``` + + + + Run the following command to clone the `framework-tutorials` repo and navigate to the folder containing the starting point for this application. + + ``` + git clone https://github.com/writer/framework-tutorials.git + cd framework-tutorials/release-notes-generator/start + ``` + + + To edit your project, run the following commands. This will bring up the console, which displays Framework-wide messages and errors, including logs from the API. By default, the Writer Framework Builder is accessible at `localhost:3006`. If this port is in use, you can specify a different port. Open this address in your browser to view your default application setup. + + + ```bash Standard port + writer edit . + ``` + + ```bash Custom port + writer edit . --port=3007 + ``` + + + + +## Introduction to the application setup +The template includes some basic code, UI setup, and files to help you get started. + +### Included files +The files `prompts.py` and `html_template.py` contain helper functions for generating prompts for the AI model and formatting the output HTML, respectively. + +In the `sample-input` folder, you'll find a sample CSV file that you can use to test the application. + +Finally, the `custom.css` file in the `static` folder contains custom CSS styles for the application. + +### Dependency imports +In `main.py`, you'll see that the dependencies are already imported at the top: + +```python +import writer as wf +import writer.ai +import pandas as pd +from prompts import get_release_notes_summary_prompt, get_release_notes_desc_prompt, get_category_prompt +from html_template import format_output +``` + +These dependencies include the Writer Framework, the Writer AI module, and pandas for data manipulation. + +### Initial UI +The template includes a basic UI setup, including a Page component with a Header component. The Header component also includes an Image. If you want to change the logo image, you can replace the `logo_image_path` variable in the state with the path to your desired image in the `static` folder. + +## Building the release notes generator backend + +First, you'll create the backend for the release notes generator. Note that `prompts.py`, `html_template.py`, and `custom.css` are provided by the template used to generate the application. + +### Initializing the application state +First, set up the initial state for the application. This state will store the application's title, logo image path, file data, metrics, and processing status for each step. You'll also import a custom CSS file to style the application and create a placeholder DataFrame. + + + Create a placeholder DataFrame: + ```python + placeholder_data = { + 'Description': ['Description 1', 'Description 2', 'Description 3'], + 'Label': ['Label 1', 'Label 2', 'Label 3'] + } + initial_df = pd.DataFrame(placeholder_data) + ``` + + + Set up the initial state for the application: + ```python + initial_state = wf.init_state({ + "my_app": {"title": "MY APP"}, + "logo_image_path": 'static/Writer_Logo_black.svg', + "file": {"name": "", "file_path": ""}, + "metrics": {"new_features": 0, "caveats": 0, "fixed_issues": 0, "total": 0}, + "step1": { + "raw_csv": initial_df, + "completed": "no", + "generate-button-state": "yes", + "processing-message": None, + "styled-table": "

csv table

" + }, + "step2": { + "release-notes": None, + "completed": "no", + "formatted-release-notes": "notes should go here" + }, + }) + ``` +
+ + Import the custom CSS file below the initial state setup: + + ```python + initial_state.import_stylesheet(".description, .list, .summary, .category ", "/static/custom.css") + ``` + +
+ + +### Defining text completion functions +Next, using the prompts provided, define functions to get the category, release notes summary, and release notes description using AI completion. You'll use these functions to process the uploaded CSV file and generate release notes. + + + Define a function to get the category using AI completion: + ```python + def _get_category(desc, label): + prompt = get_category_prompt(desc, label) + label = writer.ai.complete(prompt) + return label + ``` + + + Define a function to get the release notes summary: + ```python + def _get_release_notes_summary(label, desc): + prompt = get_release_notes_summary_prompt(label, desc) + formatted_desc = writer.ai.complete(prompt) + return formatted_desc + ``` + + + Define a function to get the release notes description: + ```python + def _get_release_notes_desc(label, desc): + prompt = get_release_notes_desc_prompt(label, desc) + formatted_desc = writer.ai.complete(prompt) + return formatted_desc + ``` + + + +### Implementing file upload handler +Next, create a function to handle file uploads. This function will read the uploaded CSV file, process the data, and store it in the application state. + +```python +def onchangefile_handler(state, payload): + uploaded_file = payload[0] + name = uploaded_file.get("name") + state["file"]["name"] = name + state["step1"]["processing-message"] = f'+File {name} uploaded successfully.' + state["file"]["file_path"] = f"data/{name}" + file_data = uploaded_file.get("data") + with open(f"data/{name}", "wb") as file_handle: + file_handle.write(file_data) + + data = pd.read_csv(state["file"]["file_path"]) + df = pd.DataFrame(data) + state["step1"]["raw_csv"] = df + state["step1"]["generate-button-state"] = "no" +``` + +### Implementing helper functions +Next, define helper functions to handle back button clicks, write HTML to a file, download the HTML file, and generate HTML for each category. + + + Define a function to handle the back button click: + ```python + def handle_back_button_click(state): + state["step1"]["completed"] = "no" + ``` + + + Define a function to write HTML to a file: + ```python + def _write_html_to_file(html): + with open("data/output-html.html", "w") as file_handle: + file_handle.write(html) + ``` + + + Define a function to handle downloading the HTML file: + ```python + def handle_file_download(state): + html_data = wf.pack_file("data/output-html.html","text/html") + file_name = "output-html.html" + state.file_download(html_data,file_name) + ``` + + + Define a function to generate HTML for each category: + ```python + def _create_df_for_category(df,state): + unique_categories = df['Primary-Category'] + formatted_output_list = list() + for category in set(unique_categories): + df_category = df[df['Primary-Category']==category] + categories = {" New Feature": "new_features", " Caveat": "caveats", " Fixed Issue": "fixed_issues" } + curr_category = categories[category] + state["metrics"][curr_category]= df_category.shape[0] + formatted_output = format_output(category,df_category) + formatted_output_list.append(formatted_output) + return "".join(formatted_output_list) + ``` + + + Define a function to convert the CSV file to a DataFrame: + ```python + def _raw_csv_to_df(state): + data = pd.read_csv(state["file"]["file_path"]) + df = pd.DataFrame(data) + return df + ``` + + + +### Implementing the generate button handler +Finally, create a function to handle the generate button click. This function will process the uploaded CSV file, generate release notes, and store the formatted output in the application state. + +```python +def handle_generate_button_click(state): + state["step1"]["generate-button-state"] = "yes" + state["step1"]["processing-message-isVisible"] = True + state["step1"]["processing-message"] = "%Hang tight, preparing to process your file" + + notes_counter = 0 + df = _raw_csv_to_df(state) + csv_row_count = df.shape[0] + for index, row in df.iterrows(): + df.at[index,"Primary-Category"] = _get_category(label=row["Labels"], desc=row["Description"]) + df.at[index,"Release-Notes-Summary"] = _get_release_notes_summary(label=row["Labels"], desc=row["Description"]) + df.at[index,"Release-Notes-Description"] = _get_release_notes_desc(label=row["Labels"], desc=row["Description"]) + notes_counter += 1 + state["step1"]["processing-message"] = f'%Processing {notes_counter} of {csv_row_count} Release Notes' + + df_temp = df[["Primary-Category","Release-Notes-Summary","Release-Notes-Description"]] + df_sorted = df_temp.sort_values(by='Primary-Category') + + state["step2"]["release-notes"] = df_sorted + state["step1"]["completed"] = "yes" + state["step1"]["processing-message"] = "" + + html = _create_df_for_category(df_sorted,state) + _write_html_to_file(html) + state["step2"]["formatted-release-notes"] = html + state["metrics"]["total"] = df_sorted.shape[0] + + state["step1"]["generate-button-state"] = "no" +``` + +This completes the backend implementation. Next, you'll build the UI for the release notes generator. + +## Building the UI +With the code complete, you can now build the UI for the release notes generator. + +### Creating the main steps + +Add a Steps component to the Page. This will contain two main steps: "Load a CSV file" and "Release notes". + +#### Loading a CSV file step +First, you'll first build the UI for the "Load a CSV file" step. + + + + Create the first Step component and name it "Load a CSV file". Set IsCompleted to `@{step1.completed}`. + + + Within this Step, add a Message component with the message set to `@{step1.processing-message}`. Scroll down to the Visibility section of the settings. Select "Custom" and set the condition to `step1.processing-message`. + + + Add a Column Container component and add three Column components. For the first column, set the width to 0.5. For the third column, set "Content alignment (V)" to "Left" and "Content alignment (H)" to "Bottom." + + + In the middle column, place a File Input component labeled "Please upload your CSV file". Set its `wf-file-change` handler to `onchangefile_handler`. + + Below it, add another Message component displaying "+@\{file.name}". + + + In the second column, add a Button labeled "Generate release notes". Set its "Disabled" property to `@{step1.generate-button-state}` and its `wf-click` handler to `handle_generate_button_click`. You can also set the "Icon" property to `laps`. + + + Under the columns, create a Section component and set the title to "Raw CSV". + + + In this section, add a DataFrame component to display the raw CSV data. Configure its properties to use `@{step1.raw_csv}` as the data source. Toggle "Enable download," "Use Markdown," and "Text wrapping" to "yes". Set the Separator color to `#d4b2f7`. + + + +Your application should now look like this: + +![Release notes generator UI](/framework/images/tutorial/release_notes/release_gen_4.png) + +When using the sample data, the Raw CSV section will display the uploaded CSV file: + +![Raw CSV section](/framework/images/tutorial/release_notes/release_gen_5.png) + +#### Release notes step +Next, you'll build the UI for the "Release notes" step. To display the Release notes Step component, you'll need to double-click on it. + + + Create the second Step component and name it "Release notes". + + + Begin with a Separator component. + + + Follow with a Column Container component and a single Column component. + + + In the column, add a Button. Set its text to "Back" and set its `wf-click handler` to `handle_back_button_click`. Set the "Icon" property to `arrow_back`. + + + +### Tabs for Release notes display + +Below the Back button, add a Tabs component with two tabs: "Formatted release notes" and "Release notes". + +#### Formatted release notes tab +In the first tab, you'll display the formatted release notes. + + + In the first tab, add an HTML component. Set the "Element" property `div` and the "Styles" property to the following object: + + ``` + { + "padding": "16px", + "min-height": "64px", + "min-width": "64px", + "border-radius": "8px", + "background": "white", + } + ``` + + Finally, set the "HTML inside" property to `@{step2.formatted-release-notes}`. + + + Inside this component, create a three-column layout using a Column Container component and three Column components. + + + In each column, add three Metric components to display new features, caveats, and fixed issues, respectively. + Set the values of these components to `@{metrics.new_features}`, `@{metrics.caveats}`, and `@{metrics.fixed_issues}`. Then, set the Note text to "+New Features", "+Caveats", and "+Fixed Issues" respectively. The "+" sign will display styling that indicates a positive message. + + + Under the columns, add a "Download HTML" Button with its `wf-click` handler set to `handle_file_download`. + + + +The Formatted release notes tab should look like this when using the sample data: + +![Formatted release notes tab](/framework/images/tutorial/release_notes/release_gen_1.png) + +#### Release notes tab +Finally, you'll add a Dataframe component to the second tab to display the detailed release notes. + + + In the second tab, start with a Metric component to show the total number of release notes generated. Set the "Name" to "Number of release notes generated" and the "Value" to `@{metrics.total}`. + + + Follow this with a DataFrame component to display the detailed release notes, setting the "Data" property to `@{step2.release-notes}`. Configure it for text wrapping, downloading, and searching capabilities. Set the Separator color to `#d4b2f7`. + + + +The final Release notes section should look like this: + +![Release notes final UI](/framework/images/tutorial/release_notes/release_gen_6.png) + +You can [see the finished code on GitHub](https://github.com/writer/framework-tutorials/release-notes-generator/end) or in `framework-tutorials/release-notes-generator/end` in the `tutorials` repo you cloned at the beginning of the tutorial. + +## Deploying the application + +To deploy the application to the Writer cloud, either terminate your current Writer Framework process or open a new terminal session and run the following command: + +``` +writer deploy . +``` + +You'll be prompted for your API key. + +Once the application is deployed, the CLI will return with the URL of your live application. + +## Conclusion +By following these steps, you've created a complete Release notes generator application using Writer Framework. To learn more, explore the rest of the Writer Framework documentation and the API documentation. \ No newline at end of file diff --git a/docs/mint.json b/docs/mint.json index 378d4a9c0..15e0eab03 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -53,7 +53,8 @@ "pages": [ "framework/chat-assistant", "framework/social-post-generator", - "framework/product-description-generator" + "framework/product-description-generator", + "framework/release-notes-generator" ] }, {