File size: 21,768 Bytes
6a9c9f9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Requirements Generator and MoSCoW Prioritization\n",
    "**Author:** Gael Sánchez\n",
    "**LinkedIn:** www.linkedin.com/in/gaelsanchez\n",
    "\n",
    "This notebook generates and validates functional and non-functional software requirements from a natural language description, and classifies them using the MoSCoW prioritization technique.\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## What is a MoSCoW Matrix?\n",
    "\n",
    "The MoSCoW Matrix is a prioritization technique used in software development to categorize requirements based on their importance and urgency. The acronym stands for:\n",
    "\n",
    "- **Must Have** – Critical requirements that are essential for the system to function.  \n",
    "- **Should Have** – Important requirements that add significant value, but are not critical for initial delivery.  \n",
    "- **Could Have** – Nice-to-have features that can enhance the product, but are not necessary.  \n",
    "- **Won’t Have (for now)** – Low-priority features that will not be implemented in the current scope.\n",
    "\n",
    "This method helps development teams make clear decisions about what to focus on, especially when working with limited time or resources. It ensures that the most valuable and necessary features are delivered first, contributing to better project planning and stakeholder alignment.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## How it works\n",
    "\n",
    "This notebook uses the OpenAI library (via the Gemini API) to extract and validate software requirements from a natural language description. The workflow follows these steps:\n",
    "\n",
    "1. **Initial Validation**  \n",
    "   The user provides a textual description of the software. The model evaluates whether the description contains enough information to derive meaningful requirements. Specifically, it checks if the description answers key questions such as:\n",
    "   \n",
    "   - What is the purpose of the software?  \n",
    "   - Who are the intended users?  \n",
    "   - What are the main features and functionalities?  \n",
    "   - What platform(s) will it run on?  \n",
    "   - How will data be stored or persisted?  \n",
    "   - Is authentication/authorization needed?  \n",
    "   - What technologies or frameworks will be used?  \n",
    "   - What are the performance expectations?  \n",
    "   - Are there UI/UX principles to follow?  \n",
    "   - Are there external integrations or dependencies?  \n",
    "   - Will it support offline usage?  \n",
    "   - Are advanced features planned?  \n",
    "   - Are there security or privacy concerns?  \n",
    "   - Are there any constraints or limitations?  \n",
    "   - What is the timeline or development roadmap?\n",
    "\n",
    "   If the description lacks important details, the model requests the missing information from the user. This loop continues until the model considers the description complete.\n",
    "\n",
    "2. **Summarization**  \n",
    "   Once validated, the model summarizes the software description, extracting its key aspects to form a concise and informative overview.\n",
    "\n",
    "3. **Requirements Generation**  \n",
    "   Using the summary, the model generates a list of functional and non-functional requirements.\n",
    "\n",
    "4. **Requirements Validation**  \n",
    "   A separate validation step checks if the generated requirements are complete and accurate based on the summary. If not, the model provides feedback, and the requirements are regenerated accordingly. This cycle repeats until the validation step approves the list.\n",
    "\n",
    "5. **MoSCoW Prioritization**  \n",
    "   Finally, the validated list of requirements is classified using the MoSCoW prioritization technique, grouping them into:\n",
    "   \n",
    "   - Must have  \n",
    "   - Should have  \n",
    "   - Could have  \n",
    "   - Won't have for now\n",
    "\n",
    "The output is a clear, structured requirements matrix ready for use in software development planning.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example Usage\n",
    "\n",
    "### Input\n",
    "\n",
    "**Software Name:** Personal Task Manager  \n",
    "**Initial Description:**  \n",
    "This will be a simple desktop application that allows users to create, edit, mark as completed, and delete daily tasks. Each task will have a title, an optional description, a due date, and a status (pending or completed). The goal is to help users organize their activities efficiently, with an intuitive and minimalist interface.\n",
    "\n",
    "**Main Features:**\n",
    "\n",
    "- Add new tasks  \n",
    "- Edit existing tasks  \n",
    "- Mark tasks as completed  \n",
    "- Delete tasks  \n",
    "- Filter tasks by status or date\n",
    "\n",
    "**Additional Context Provided After Model Request:**\n",
    "\n",
    "- **Intended Users:** Individuals seeking to improve their daily productivity, such as students, remote workers, and freelancers.  \n",
    "- **Platform:** Desktop application for common operating systems.  \n",
    "- **Data Storage:** Tasks will be stored locally.  \n",
    "- **Authentication/Authorization:** A lightweight authentication layer may be included for data protection.  \n",
    "- **Technology Stack:** Cross-platform technologies that support a modern, functional UI.  \n",
    "- **Performance:** Expected to run smoothly with a reasonable number of active and completed tasks.  \n",
    "- **UI/UX:** Prioritizes a simple, modern user experience.  \n",
    "- **Integrations:** Future integration with calendar services is considered.  \n",
    "- **Offline Usage:** The application will work without an internet connection.  \n",
    "- **Advanced Features:** Additional features like notifications or recurring tasks may be added in future versions.  \n",
    "- **Security/Privacy:** User data privacy will be respected and protected.  \n",
    "- **Constraints:** Focus on simplicity, excluding complex features in the initial version.  \n",
    "- **Timeline:** Development planned in phases, starting with a functional MVP.\n",
    "\n",
    "### Output\n",
    "\n",
    "**MoSCoW Prioritization Matrix:**\n",
    "\n",
    "**Must Have**\n",
    "- Task Creation: [The system needs to allow users to add tasks to be functional.]  \n",
    "- Task Editing: [Users must be able to edit tasks to correct mistakes or update information.]  \n",
    "- Task Completion: [Marking tasks as complete is a core function of a task management system.]  \n",
    "- Task Deletion: [Users need to be able to remove tasks that are no longer relevant.]  \n",
    "- Task Status: [Maintaining task status (pending/completed) is essential for tracking progress.]  \n",
    "- Data Persistence: [Tasks must be stored to be useful beyond a single session.]  \n",
    "- Performance: [The system needs to perform acceptably for a reasonable number of tasks.]  \n",
    "- Usability: [The system must be easy to use for all other functionalities to be useful.]\n",
    "\n",
    "**Should Have**\n",
    "- Task Filtering by Status: [Filtering enhances usability and allows users to focus on specific tasks.]  \n",
    "- Task Filtering by Date: [Filtering by date helps manage deadlines.]  \n",
    "- User Interface Design: [A modern design improves user experience.]  \n",
    "- Platform Compatibility: [Running on common OSes increases adoption.]  \n",
    "- Data Privacy: [Important for user trust, can be gradually improved.]  \n",
    "- Security: [Basic protections are necessary, advanced features can wait.]\n",
    "\n",
    "**Could Have**\n",
    "- Optional Authentication: [Enhances security but adds complexity.]  \n",
    "- Offline Functionality: [Convenient, but not critical for MVP.]\n",
    "\n",
    "**Won’t Have (for now)**\n",
    "- N/A: [No features were excluded completely at this stage.]\n",
    "\n",
    "---\n",
    "\n",
    "This example demonstrates how the notebook takes a simple description and iteratively builds a complete and validated set of software requirements, ultimately organizing them into a MoSCoW matrix for development planning.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from openai import OpenAI\n",
    "from pydantic import BaseModel\n",
    "import gradio as gr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "load_dotenv(override=True)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "gemini = OpenAI(\n",
    "    api_key=os.getenv(\"GOOGLE_API_KEY\"), \n",
    "    base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\"\n",
    ")\n",
    "    \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "class StandardSchema(BaseModel):\n",
    "    understood: bool\n",
    "    feedback: str\n",
    "    output: str"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "# This is the prompt to validate the description of the software product on the first step\n",
    "system_prompt = f\"\"\"\n",
    "        You are a software analyst. the user will give you a description of a software product. Your task is to decide the description provided is complete and accurate and useful to derive requirements for the software.\n",
    "        If you decide the description is not complete or accurate, you should provide a kind message to the user listing the missing or incorrect information, and ask them to provide the missing information.\n",
    "        If you decide the description is complete and accurate, you should provide a summary of the description in a structured format. Only provide the summary, nothing else.\n",
    "        Ensure that the description answers the following questions:\n",
    "        - What is the purpose of the software?\n",
    "        - Who are the intended users?\n",
    "        - What are the main features and functionalities of the software?\n",
    "        - What platform(s) will it run on?\n",
    "        - How will data be stored or persisted?\n",
    "        - Is user authentication or authorization required?\n",
    "        - What technologies or frameworks will be used?\n",
    "        - What are the performance expectations?\n",
    "        - Are there any UI/UX design principles that should be followed?\n",
    "        - Are there any external integrations or dependencies?\n",
    "        - Will it support offline usage?\n",
    "        - Are there any planned advanced features?\n",
    "        - Are there any security or privacy considerations?\n",
    "        - Are there any constrains or limitations?\n",
    "        - What is the desired timeline or development roadmap?\n",
    "\n",
    "        Respond in the following format:\n",
    "        \n",
    "        \"understood\": true only if the description is complete and accurate\n",
    "        \"feedback\": Instructions to the user to provide the missing or incorrect information.\n",
    "        \"output\": Summary of the description in a structured format, once the description is complete and accurate.\n",
    "        \n",
    "    \"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "# This function is used to validate the description and provide feedback to the user.\n",
    "# It receives the messages from the user and the system prompt.\n",
    "# It returns the validation response.\n",
    "\n",
    "def validate_and_feedback(messages):\n",
    "\n",
    "    validation_response = gemini.beta.chat.completions.parse(model=\"gemini-2.0-flash\", messages=messages, response_format=StandardSchema)\n",
    "    validation_response = validation_response.choices[0].message.parsed\n",
    "    return validation_response\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "# This function is used to validate the requirements and provide feedback to the model.\n",
    "# It receives the description and the requirements.\n",
    "# It returns the validation response.\n",
    "\n",
    "def validate_requirements(description, requirements):\n",
    "    validator_prompt = f\"\"\"\n",
    "        You are a software requirements reviewer.\n",
    "        Your task is to analyze a set of functional and non-functional requirements based on a given software description.\n",
    "\n",
    "        Perform the following validation steps:\n",
    "\n",
    "        Completeness: Check if all key features, fields, and goals mentioned in the description are captured as requirements.\n",
    "\n",
    "        Consistency: Verify that all listed requirements are directly supported by the description. Flag anything that was added without justification.\n",
    "\n",
    "        Clarity & Redundancy: Identify requirements that are vague, unclear, or redundant.\n",
    "\n",
    "        Missing Elements: Highlight important elements from the description that were not translated into requirements.\n",
    "\n",
    "        Suggestions: Recommend improvements or additional requirements that better align with the description.\n",
    "\n",
    "        Answer in the following format:\n",
    "        \n",
    "        \"understood\": true only if the requirements are complete and accurate,\n",
    "        \"feedback\": Instructions to the generator to improve the requirements.\n",
    "        \n",
    "        Here's the software description:\n",
    "        {description}\n",
    "\n",
    "        Here's the requirements:\n",
    "        {requirements}\n",
    "\n",
    "    \"\"\"\n",
    "\n",
    "    validator_response = gemini.beta.chat.completions.parse(model=\"gemini-2.0-flash\", messages=[{\"role\": \"user\", \"content\": validator_prompt}], response_format=StandardSchema)\n",
    "    validator_response = validator_response.choices[0].message.parsed\n",
    "    return validator_response\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "# This function is used to generate a rerun prompt for the requirements generator.\n",
    "# It receives the description, the requirements and the feedback.\n",
    "# It returns the rerun prompt.\n",
    "\n",
    "def generate_rerun_requirements_prompt(description, requirements, feedback):\n",
    "    return f\"\"\"\n",
    "    You are a software analyst. Based on the following software description, you generated the following list of functional and non-functional requirements. \n",
    "    However, the requirements validator rejected the list, with the following feedback. Please review the feedback and improve the list of requirements.\n",
    "\n",
    "    ## Here's the description:\n",
    "    {description}\n",
    "\n",
    "    ## Here's the requirements:\n",
    "    {requirements}\n",
    "\n",
    "    ## Here's the feedback:\n",
    "    {feedback}\n",
    "    \"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "# This function generates the requirements based on the description.\n",
    "def generate_requirements(description):\n",
    "    generator_prompt = f\"\"\"\n",
    "    You are a software analyst. Based on the following software description, generate a comprehensive list of both functional and non-functional requirements.\n",
    "\n",
    "    The requirements must be clear, actionable, and written in concise natural language.\n",
    "\n",
    "    Each requirement should describe exactly what the system must do or how it should behave, with enough detail to support MoSCoW prioritization and later transformation into user stories.\n",
    "\n",
    "    Group the requirements into two sections: Functional Requirements and Non-Functional Requirements.\n",
    "\n",
    "    Avoid redundancy. Do not include implementation details unless they are part of the expected behavior.\n",
    "\n",
    "    Write in professional and neutral English.\n",
    "\n",
    "    Output in Markdown format.\n",
    "\n",
    "    Answer in the following format:\n",
    "\n",
    "    \"understood\": true\n",
    "    \"output\": List of requirements\n",
    "\n",
    "    ## Here's the description:\n",
    "    {description}\n",
    "\n",
    "    ## Requirements:\n",
    "    \"\"\"\n",
    "\n",
    "    requirements_response = gemini.beta.chat.completions.parse(model=\"gemini-2.0-flash\", messages=[{\"role\": \"user\", \"content\": generator_prompt}], response_format=StandardSchema)\n",
    "    requirements_response = requirements_response.choices[0].message.parsed\n",
    "    requirements = requirements_response.output\n",
    "\n",
    "    requirements_valid = validate_requirements(description, requirements)\n",
    "    \n",
    "    # Validation loop\n",
    "    while not requirements_valid.understood:\n",
    "        rerun_requirements_prompt = generate_rerun_requirements_prompt(description, requirements, requirements_valid.feedback)\n",
    "        requirements_response = gemini.beta.chat.completions.parse(model=\"gemini-2.0-flash\", messages=[{\"role\": \"user\", \"content\": rerun_requirements_prompt}], response_format=StandardSchema)\n",
    "        requirements_response = requirements_response.choices[0].message.parsed\n",
    "        requirements = requirements_response.output\n",
    "        requirements_valid = validate_requirements(description, requirements)\n",
    "\n",
    "    return requirements\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "# This function generates the MoSCoW priorization of the requirements.\n",
    "# It receives the requirements.\n",
    "# It returns the MoSCoW priorization.\n",
    "\n",
    "def generate_moscow_priorization(requirements):\n",
    "    priorization_prompt = f\"\"\"\n",
    "    You are a product analyst.\n",
    "    Based on the following list of functional and non-functional requirements, classify each requirement into one of the following MoSCoW categories:\n",
    "\n",
    "    Must Have: Essential requirements that the system cannot function without.\n",
    "\n",
    "    Should Have: Important requirements that add significant value but are not absolutely critical.\n",
    "\n",
    "    Could Have: Desirable but non-essential features, often considered nice-to-have.\n",
    "\n",
    "    Won’t Have (for now): Requirements that are out of scope for the current version but may be included in the future.\n",
    "\n",
    "    For each requirement, place it under the appropriate category and include a brief justification (12 sentences) explaining your reasoning.\n",
    "\n",
    "    Format your output using Markdown, like this:\n",
    "\n",
    "    ## Must Have\n",
    "    - [Requirement]: [Justification]\n",
    "\n",
    "    ## Should Have\n",
    "    - [Requirement]: [Justification]\n",
    "\n",
    "    ## Could Have\n",
    "    - [Requirement]: [Justification]\n",
    "\n",
    "    ## Won’t Have (for now)\n",
    "    - [Requirement]: [Justification]\n",
    "\n",
    "    ## Here's the requirements:\n",
    "    {requirements}\n",
    "    \"\"\"\n",
    "\n",
    "    priorization_response = gemini.beta.chat.completions.parse(model=\"gemini-2.0-flash\", messages=[{\"role\": \"user\", \"content\": priorization_prompt}], response_format=StandardSchema)\n",
    "    priorization_response = priorization_response.choices[0].message.parsed\n",
    "    priorization = priorization_response.output\n",
    "    return priorization\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "def chat(message, history):\n",
    "    messages = [{\"role\": \"system\", \"content\": system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n",
    "\n",
    "    validation =validate_and_feedback(messages)\n",
    "\n",
    "    if not validation.understood:\n",
    "        print('retornando el feedback')\n",
    "        return validation.feedback\n",
    "    else:\n",
    "        requirements = generate_requirements(validation.output)\n",
    "        moscow_prioritization = generate_moscow_priorization(requirements)\n",
    "        return moscow_prioritization\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "gr.ChatInterface(chat, type=\"messages\").launch()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}