diff --git a/.github/workflows/generate-pdfs-on-release.yml b/.github/workflows/generate-pdfs-on-release.yml index 4056ffb..4ba49ef 100644 --- a/.github/workflows/generate-pdfs-on-release.yml +++ b/.github/workflows/generate-pdfs-on-release.yml @@ -42,9 +42,8 @@ jobs: # Merge all PDFs into one echo "Merging PDFs..." - pdfunite pdfs/*.pdf corso-python-basic.pdf - find pdfs -type f -name "*.pdf" ! -name "corso-python-basic.pdf" -delete - echo "✅ Conversion complete! Final merged file: corso-python-basic.pdf" + pdfunite pdfs/*.pdf pdfs/corso-python-basic.pdf + echo "Conversion complete! Final merged file: corso-python-basic.pdf" - name: Upload PDFs to release env: diff --git a/00_sommario_corso.ipynb b/00_sommario_corso.ipynb index 2c00c23..821078f 100755 --- a/00_sommario_corso.ipynb +++ b/00_sommario_corso.ipynb @@ -65,7 +65,7 @@ "id": "footer", "metadata": {}, "source": [ - "© 2025 Hanamai. All rights reserved. | Built with precision for real-time data streaming excellence." + "© 2025 hanam.ai - All rights reserved. | Built with precision for real-time data streaming excellence." ] } ], diff --git a/01_primi_passi.ipynb b/01_primi_passi.ipynb index fd4de08..cc5d36f 100755 --- a/01_primi_passi.ipynb +++ b/01_primi_passi.ipynb @@ -423,17 +423,9 @@ "metadata": {}, "source": [ "### Esercizio 1: Saluto personalizzato\n", - "Modificare il file `greet.py` per chiedere il nome e stampare un messaggio di benvenuto più elaborato." + "Modificare il file `greet.py` trovato nella lezione per chiedere il nome e stampare un messaggio di benvenuto più elaborato." ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "4895b613", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "id": "92adf2b9", @@ -443,14 +435,6 @@ "Scrivere un nuovo script chiamato `calculate_square_root.py` che usa la libreria `math` per chiedere un numero all'utente e stampare la sua radice quadrata." ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "35888dbe", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "id": "5d9176d4", @@ -461,24 +445,18 @@ ] }, { - "cell_type": "code", - "execution_count": null, - "id": "59ace534", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "b3f86e9e", - "metadata": {}, - "source": [ + "cell_type": "markdown", + "id": "72c70c52", + "metadata": {}, + "source": [ "---\n", - "## Soluzioni\n", + "## 💡 Soluzioni\n", + "\n", + "> 📂 **[Clicca qui per vedere il codice delle soluzioni](code/01/solutions)**\n", "\n", "---" - ] - }, + ] +}, { "cell_type": "markdown", "id": "4895b614", @@ -495,7 +473,7 @@ "outputs": [], "source": [ "def greet_user():\n", - " user_name = input(\"Wha is your name? \")\n", + " user_name = input(\"What is your name? \")\n", " print(f\"Hello {user_name}! It is a real pleasure to have you here.\")\n", "\n", "if __name__ == \"__main__\":\n", @@ -557,7 +535,7 @@ "id": "footer", "metadata": {}, "source": [ - "© 2025 Hanamai. All rights reserved. | Built with precision for real-time data streaming excellence." + "© 2025 hanam.ai - All rights reserved. | Built with precision for real-time data streaming excellence." ] } ], diff --git a/02_sintassi_base.ipynb b/02_sintassi_base.ipynb index 20172ce..eeec1b5 100755 --- a/02_sintassi_base.ipynb +++ b/02_sintassi_base.ipynb @@ -540,16 +540,25 @@ "- Continuare a chiedere finché non indovina." ] }, + { + "cell_type": "markdown", + "id": "72c70c51", + "metadata": {}, + "source": [ + "---\n", + "## 💡 Soluzioni\n", + "\n", + "> 📂 **[Clicca qui per vedere il codice delle soluzioni](code/02/solutions)**\n", + "\n", + "---" + ] +}, { "cell_type": "markdown", - "id": "72c70c51", + "id": "4895b614", "metadata": {}, "source": [ - "---\n", - "## Soluzioni\n", - "\n", - "---\n", - "### Soluzione Esercizio 1: Variabili e tipi\n" + "### Soluzione Esercizio 1: Variabili e tipi" ] }, { @@ -559,9 +568,13 @@ "metadata": {}, "outputs": [], "source": [ - "user_name = \"Luca\"\n", - "age = 25\n", - "print(f\"Hi, my name is {user_name} and I am {age} years old.\")" + "def greet_user():\n", + " user_name = \"Luca\"\n", + " age = 25\n", + " print(f\"Hi, my name is {user_name} and I am {age} years old.\")\n", + "\n", + "if __name__ == \"__main__\":\n", + " greet_user()" ] }, { @@ -579,14 +592,19 @@ "metadata": {}, "outputs": [], "source": [ - "a = int(input(\"First number: \"))\n", - "b = int(input(\"Second number: \"))\n", - "if a > b:\n", - " print(\"First number is greater.\")\n", - "elif a < b:\n", - " print(\"First number is less.\")\n", - "else:\n", - " print(\"Numbers are equal.\")" + "def guess_numbers():\n", + " a = int(input(\"First number: \"))\n", + " b = int(input(\"Second number: \"))\n", + " if a > b:\n", + " print(\"First number is greater.\")\n", + " elif a < b:\n", + " print(\"First number is less.\")\n", + " else:\n", + " print(\"Numbers are equal.\")\n", + " print(f\"Hi, my name is {user_name} and I am {age} years old.\")\n", + "\n", + "if __name__ == \"__main__\":\n", + " guess_numbers()" ] }, { @@ -604,13 +622,17 @@ "metadata": {}, "outputs": [], "source": [ - "grade = int(input(\"Enter a grade: \"))\n", - "if grade >= 60:\n", - " print(\"Passed\")\n", - "elif grade >= 40:\n", - " print(\"Remedial\")\n", - "else:\n", - " print(\"Failed\")" + "def ask_grade():\n", + " grade = int(input(\"Enter a grade: \"))\n", + " if grade >= 60:\n", + " print(\"Passed\")\n", + " elif grade >= 40:\n", + " print(\"Remedial\")\n", + " else:\n", + " print(\"Failed\")\n", + "\n", + "if __name__ == \"__main__\":\n", + " ask_grade()" ] }, { @@ -628,8 +650,12 @@ "metadata": {}, "outputs": [], "source": [ - "for i in range(1, 11):\n", - " print(i)" + "def print_numbers():\n", + " for i in range(1, 11):\n", + " print(i)\n", + "\n", + "if __name__ == \"__main__\":\n", + " print_numbers()" ] }, { @@ -647,14 +673,18 @@ "metadata": {}, "outputs": [], "source": [ - "secret_number = 7\n", - "while True:\n", - " guess = int(input(\"Guuess a number: \"))\n", - " if guess == secret_number:\n", - " print(\"You guessed it!\")\n", - " break\n", - " else:\n", - " print(\"Try again.\")" + "def guess_number():\n", + " secret_number = 7\n", + " while True:\n", + " guess = int(input(\"Guess a number: \"))\n", + " if guess == secret_number:\n", + " print(\"You guessed it!\")\n", + " break\n", + " else:\n", + " print(\"Try again.\")\n", + "\n", + "if __name__ == \"__main__\":\n", + " guess_number()" ] }, { @@ -662,7 +692,7 @@ "id": "footer", "metadata": {}, "source": [ - "© 2025 Hanamai. All rights reserved. | Built with precision for real-time data streaming excellence." + "© 2025 hanam.ai - All rights reserved. | Built with precision for real-time data streaming excellence." ] } ], diff --git a/03_strutture_dati.ipynb b/03_strutture_dati.ipynb index 627a476..3ad2a7b 100755 --- a/03_strutture_dati.ipynb +++ b/03_strutture_dati.ipynb @@ -698,18 +698,19 @@ "Usare una *generator expression* per calcolare il cubo dei numeri da 0 a 4 e stampare i risultati in un ciclo `for`." ] }, - { - "cell_type": "markdown", - "id": "8b2811d0", - "metadata": {}, - "source": [ - "---", + { + "cell_type": "markdown", + "id": "72c70c53", + "metadata": {}, + "source": [ + "---\n", + "## 💡 Soluzioni\n", "\n", - "## Soluzioni\n", + "> 📂 **[Clicca qui per vedere il codice delle soluzioni](code/03/solutions)**\n", "\n", "---" - ] - }, + ] +}, { "cell_type": "markdown", "metadata": {}, @@ -723,13 +724,17 @@ "metadata": {}, "outputs": [], "source": [ - "colours = ['yellow', 'orange', 'violet']\n", - "coordinates = (45, 90)\n", - "person = {'name': 'Paolo', 'city': 'Milano'}\n", + "def details():\n", + " colours = ['yellow', 'orange', 'violet']\n", + " coordinates = (45, 90)\n", + " person = {'name': 'Paolo', 'city': 'Milano'}\n", + "\n", + " print(colours[1])\n", + " print(coordinates[-1])\n", + " print(person['name'])\n", "\n", - "print(colours[1])\n", - "print(coordinates[-1])\n", - "print(person['name'])" + "if __name__ == \"__main__\":\n", + " details()" ] }, { @@ -747,10 +752,14 @@ "metadata": {}, "outputs": [], "source": [ - "url_parts = [\"https:\", \"\", \"www.python.org\", \"doc\"]\n", - "final_url = \"//\".join(url_parts[:2]) + \"/\".join(url_parts[2:])\n", + "def join_url():\n", + " url_parts = [\"https:\", \"\", \"www.python.org\", \"doc\"]\n", + " final_url = \"//\".join(url_parts[:2]) + \"/\".join(url_parts[2:])\n", "\n", - "print(final_url)" + " print(final_url)\n", + "\n", + "if __name__ == \"__main__\":\n", + " join_url()" ] }, { @@ -761,6 +770,7 @@ "### Soluzione Esercizio 3: Lista numeri\n" ] }, + { "cell_type": "code", "execution_count": null, @@ -768,9 +778,14 @@ "metadata": {}, "outputs": [], "source": [ - "numbers = [4, 1, 7, 3, 9]\n", - "numbers.append(6)\n", - "print(sorted(numbers))\n" + "def add_number():\n", + " numbers = [4, 1, 7, 3, 9]\n", + " numbers.append(6)\n", + "\n", + " print(sorted(numbers))\n", + "\n", + "if __name__ == \"__main__\":\n", + " add_number()" ] }, { @@ -788,8 +803,12 @@ "metadata": {}, "outputs": [], "source": [ - "coordinates = (12.5, 8.2)\n", - "print(coordinates[0])\n" + "def coordinates():\n", + " coordinates = (12.5, 8.2)\n", + " print(coordinates[0])\n", + "\n", + "if __name__ == \"__main__\":\n", + " coordinates()" ] }, { @@ -807,9 +826,13 @@ "metadata": {}, "outputs": [], "source": [ - "animals = {\"cat\", \"dog\"}\n", - "animals.add(\"fox\")\n", - "print(animals)\n" + "def animals():\n", + " animals = {\"cat\", \"dog\"}\n", + " animals.add(\"fox\")\n", + " print(animals)\n", + "\n", + "if __name__ == \"__main__\":\n", + " animals()" ] }, { @@ -827,8 +850,13 @@ "metadata": {}, "outputs": [], "source": [ - "student = {\"name\": \"Mario\", \"age\": 21, \"course\": \"Relational Databases\"}\n", - "print(student.values())\n" + "def dict_student():\n", + " student = {\"name\": \"Mario\", \"age\": 21, \"course\": \"Relational Databases\"}\n", + " animals.add(\"fox\")\n", + " print(*student.values()) # Python 3 .values, it returns a view object, not a list, unpack the object\n", + "\n", + "if __name__ == \"__main__\":\n", + " dict_student()" ] }, { @@ -844,20 +872,25 @@ "metadata": {}, "outputs": [], "source": [ - "colours = ['red', 'green', 'blue']\n", - "\n", - "# We obtain the iterator from the list\n", - "iter_colours = iter(colours)\n", - "\n", - "# Simulate a for loop using a while loop with try-except.\n", - "while True:\n", - " try:\n", - " colour = next(iter_colours)\n", - " print(colour)\n", - " except StopIteration:\n", - " # The iterator has exhausted its elements, so we exit the loop.\n", - " break" + "def iter_colours():\n", + " colours = ['red', 'green', 'blue']\n", + "\n", + " # We obtain the iterator from the list\n", + " iter_colours = iter(colours)\n", + "\n", + " # Simulate a for loop using a while loop with try-except.\n", + " while True:\n", + " try:\n", + " colour = next(iter_colours)\n", + " print(colour)\n", + " except StopIteration:\n", + " # The iterator has exhausted its elements, so we exit the loop.\n", + " break\n", + "\n", + "if __name__ == \"__main__\":\n", + " iter_colours()" ] + }, { "cell_type": "markdown", @@ -875,10 +908,11 @@ "def sum_three_numbers(a, b, c):\n", " return a + b + c\n", "\n", - "numbers = [10, 20, 30]\n", + "if __name__ == \"__main__\":\n", + " numbers = [10, 20, 30]", "\n", - "result = sum_three_numbers(*numbers)\n", - "print(f\"Sum is: {result}\")" + " result = sum_three_numbers(*numbers)\n", + " print(f\"Sum is: {result}\")" ] }, { @@ -897,9 +931,9 @@ "def save_user(username, password):\n", " print(f\"User {username} saved with password {password}!\")\n", "\n", - "credentials = {'username': 'admin', 'password': 'password'}\n", - "\n", - "save_user(**credentials)" + "if __name__ == \"__main__\":\n", + " credentials = {'username': 'admin', 'password': 'password'}\n", + " save_user(**credentials)" ] }, { @@ -915,9 +949,13 @@ "metadata": {}, "outputs": [], "source": [ -"squares = [x**2 for x in range(1, 11)]\n", -"print(squares)" -] + "def squares():\n", + " squares = [x**2 for x in range(1, 11)]\n", + " print(squares)\n", + "\n", + "if __name__ == \"__main__\":\n", + " squares()" + ] }, { "cell_type": "markdown", @@ -932,10 +970,14 @@ "metadata": {}, "outputs": [], "source": [ -"words = ['python', 'ai', 'data']\n", -"lengths = {word: len(word) for word in words}\n", -"print(lengths)" -] + "def words_length():\n", + " words = ['python', 'ai', 'data']\n", + " lengths = {word: len(word) for word in words}\n", + " print(lengths)\n", + "\n", + "if __name__ == \"__main__\":\n", + " words_length()" + ] }, { "cell_type": "markdown", @@ -950,10 +992,14 @@ "metadata": {}, "outputs": [], "source": [ -"words = ['python', 'ai', 'data']\n", -"first_letters = {word[0] for word in words}\n", -"print(first_letters)" -] + "def first_letter():\n", + " words = ['python', 'ai', 'data']\n", + " first_letters = {word[0] for word in words}\n", + " print(first_letters)\n", + "\n", + "if __name__ == \"__main__\":\n", + " first_letter()" + ] }, { "cell_type": "markdown", @@ -968,17 +1014,21 @@ "metadata": {}, "outputs": [], "source": [ -"gen = (x**3 for x in range(5))\n", -"for value in gen:\n", -" print(value)" -] + "def gen_numbers():\n", + " gen = (x**3 for x in range(5))\n", + " for value in gen:\n", + " print(value)\n", + "\n", + "if __name__ == \"__main__\":\n", + " gen_numbers()" + ] }, { "cell_type": "markdown", "id": "footer", "metadata": {}, "source": [ - "© 2025 Hanamai. All rights reserved. | Built with precision for real-time data streaming excellence." + "© 2025 hanam.ai - All rights reserved. | Built with precision for real-time data streaming excellence." ] } ], diff --git a/04_gestione_errori.ipynb b/04_gestione_errori.ipynb index f311afb..34df491 100755 --- a/04_gestione_errori.ipynb +++ b/04_gestione_errori.ipynb @@ -170,14 +170,18 @@ ] }, { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Soluzioni\n", - "\n", - "---" - ] - }, + "cell_type": "markdown", + "id": "72c70c54", + "metadata": {}, + "source": [ + "---\n", + "## 💡 Soluzioni\n", + "\n", + "> 📂 **[Clicca qui per vedere il codice delle soluzioni](code/04/solutions)**\n", + "\n", + "---" + ] +}, { "cell_type": "markdown", "metadata": {}, @@ -191,16 +195,20 @@ "execution_count": null, "outputs": [], "source": [ - "try:\n", - " a = float(input(\"Enter first number: \"))\n", - " b = float(input(\"Enter second number: \"))\n", - " result = a / b\n", - " print(f\"Division result is: {result}\")\n", - "except ZeroDivisionError:\n", - " print(\"Error: Division by zero.\")\n", - "except ValueError:\n", - " print(\"Error: input not valid. Enter only numbers.\")" - ] + "def divide_numbers():\n", + " try:\n", + " a = float(input(\"Enter first number: \"))\n", + " b = float(input(\"Enter second number: \"))\n", + " result = a / b\n", + " print(f\"Division result is: {result}\")\n", + " except ZeroDivisionError:\n", + " print(\"Error: Division by zero.\")\n", + " except ValueError:\n", + " print(\"Error: input not valid. Enter only numbers.\")\n", + "\n", + "if __name__ == \"__main__\":\n", + " divide_numbers()" + ] }, { "cell_type": "markdown", @@ -214,16 +222,20 @@ "metadata": {}, "execution_count": null, "outputs": [], - "source": [ - "fruits = ['apple', 'banana', 'kiwi']\n", - "try:\n", - " index = int(input(f\"Enter an index (0-{len(fruits)-1}): \"))\n", - " print(f\"Element at index {index} is: {fruits[index]}\")\n", - "except ValueError:\n", - " print(\"Error: Input is not an int.\")\n", - "except IndexError:\n", - " print(f\"Error: Index out of range (0-{len(fruits)-1}).\")" - ] + "source": [ + "def index_fruits():\n", + " fruits = ['apple', 'banana', 'kiwi']\n", + " try:\n", + " index = int(input(f\"Enter an index (0-{len(fruits)-1}): \"))\n", + " print(f\"Element at index {index} is: {fruits[index]}\")\n", + " except ValueError:\n", + " print(\"Error: Input is not an int.\")\n", + " except IndexError:\n", + " print(f\"Error: Index out of range (0-{len(fruits)-1}).\")\n", + "\n", + "if __name__ == \"__main__\":\n", + " index_fruits()" + ] }, { "cell_type": "markdown", @@ -238,20 +250,24 @@ "execution_count": null, "outputs": [], "source": [ - "try:\n", - " number_str = input(\"Enter an int: \")\n", - " number_int = int(number_str)\n", - " print(f\"Int is: {number_int}\")\n", - "except ValueError:\n", - " print(f\"Error: '{number_str}' is not a valid int.\")" - ] + "def convert_int():\n", + " try:\n", + " number_str = input(\"Enter an int: \")\n", + " number_int = int(number_str)\n", + " print(f\"Int is: {number_int}\")\n", + " except ValueError:\n", + " print(f\"Error: '{number_str}' is not a valid int.\")\n", + "\n", + "if __name__ == \"__main__\":\n", + " convert_int()" + ] }, { "cell_type": "markdown", "id": "footer", "metadata": {}, "source": [ - "© 2025 Hanamai. All rights reserved. | Built with precision for real-time data streaming excellence." + "© 2025 hanam.ai - All rights reserved. | Built with precision for real-time data streaming excellence." ] } ], diff --git a/05_funzioni_moduli.ipynb b/05_funzioni_moduli.ipynb index 783eb86..04ed03b 100755 --- a/05_funzioni_moduli.ipynb +++ b/05_funzioni_moduli.ipynb @@ -55,13 +55,11 @@ "metadata": {}, "outputs": [], "source": [ - "# Definizione di una funzione con argomenti posizionali\n", "def greet(name, surname):\n", " print(f\"Hello, {name} {surname}!\")\n", "\n", "greet(\"Mario\", \"Rossi\")\n", "\n", - "# Definizione di una funzione con argomenti nominali\n", "def animal_description(species, name):\n", " print(f\"This animal is a {species} named {name}.\")\n", "\n", @@ -265,6 +263,53 @@ "print(f\"Substract is: {result_subtract}\")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Visibilità e Scope delle Variabili 🔍\n", + "\n", + "In Python, la **visibilità** (scope) indica *dove* una variabile o una funzione è accessibile nel codice. Ogni volta che Python deve risolvere un nome (una variabile o una funzione), segue la regola **LEGB**, che specifica l'ordine di ricerca:\n", + "\n", + "| Livello | Nome | Descrizione |\n", + "|----------|------|-------------|\n", + "| **L** | Local | Variabili definite *dentro* una funzione o un blocco |\n", + "| **E** | Enclosing | Scope della funzione esterna (nelle funzioni annidate) |\n", + "| **G** | Global | Variabili definite nel modulo principale |\n", + "| **B** | Built-in | Nomi predefiniti di Python (es. `len`, `print`) |\n", + "\n", + "Esempio:\n", + "\n", + "```python\n", + "x = 'globale'\n", + "\n", + "def outer():\n", + " x = 'enclosing'\n", + " def inner():\n", + " x = 'locale'\n", + " print(x)\n", + " inner()\n", + "\n", + "outer() # Output: locale\n", + "```\n", + "\n", + "Python cerca `x` in ordine: **locale → enclosing → globale → built-in**.\n", + "\n", + "#### Funzioni e visibilità\n", + "\n", + "Le funzioni definite *dentro* altre funzioni sono visibili solo all'interno della funzione che le contiene:\n", + "\n", + "```python\n", + "def esterna():\n", + " def interna():\n", + " print('Ciao da interna')\n", + " interna() \n", + "\n", + "esterna()\n", + "interna() # ❌ Errore: NameError\n", + "```\n" + ] +}, { "cell_type": "markdown", "metadata": {}, @@ -402,13 +447,6 @@ "Creare una funzione `calculate_rectangle_area(base, height)` che restituisca l'area di un rettangolo. Chiamarla con dei valori a scelta." ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "metadata": {}, @@ -417,13 +455,6 @@ "Scrivere una funzione `greet(name, message)` che stampi un messaggio di benvenuto. L'argomento `message` deve essere opzionale, con un valore di default `Hello`." ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "metadata": {}, @@ -432,13 +463,6 @@ "Importare il modulo `datetime` e stampare la data e l'ora attuali usando `datetime.datetime.now()`." ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "metadata": {}, @@ -464,13 +488,6 @@ "Scrivere il codice per `main.py` per calcolare e stampare l'area di un rettangolo con base `10` e altezza `5`." ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "metadata": {}, @@ -479,24 +496,19 @@ "Creare una funzione `apply_format(str, format)` che prenda una stringa e una funzione `format` come argomenti. L'obiettivo è far sì che `apply_format` chiami la funzione `format` con la stringa e stampi il risultato. Poi, creare due funzioni, `lower_case` e `upper_case`, e usarle come argomenti per `apply_format`." ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "---", + { + "cell_type": "markdown", + "id": "72c70c55", + "metadata": {}, + "source": [ + "---\n", + "## 💡 Soluzioni\n", "\n", - "## Soluzioni\n", + "> 📂 **[Clicca qui per vedere il codice delle soluzioni](code/05/solutions)**\n", "\n", "---" - ] - }, + ] +}, { "cell_type": "markdown", "metadata": {}, @@ -513,8 +525,9 @@ "def calculate_rectangle_area(base, height):\n", " return base * height\n", "\n", - "area = calculate_rectangle_area(10, 5)\n", - "print(f\"The area of the rectangle is: {area}\")" + "if __name__ == \"__main__\":\n", + " area = calculate_rectangle_area(10, 5)\n", + " print(f\"The area of the rectangle is: {area}\")" ] }, { @@ -533,11 +546,9 @@ "def greet(name, message='Hello'):\n", " print(f\"{message}, {name}!\")\n", "\n", - "# Use predefined greeting\n", - "greet(\"Mario\")\n", - "\n", - "# Set a different greeting\n", - "greet(\"Ugo\", \"Good morning\")" + "if __name__ == \"__main__\":\n", + " greet(\"Mario\")\n", + " greet(\"Ugo\", \"Good morning\")" ] }, { @@ -555,8 +566,12 @@ "source": [ "import datetime\n", "\n", - "current_time = datetime.datetime.now()\n", - "print(f\"The current date and time are: {current_time}\")" + "def current_time():\n", + " current_time = datetime.datetime.now()\n", + " print(f\"The current date and time are: {current_time}\")\n", + "\n", + "if __name__ == \"__main__\":\n", + " current_time()" ] }, { @@ -565,7 +580,7 @@ "source": [ "### Soluzione Esercizio 4: Gestire la struttura di un progetto\n", "\n", - "Per risolvere l'esercizio, devi prima ricreare la struttura delle cartelle e dei file. Dalla riga di comando, puoi creare la struttura in questo modo:\n", + "Per risolvere l'esercizio, occorre prima ricreare la struttura delle cartelle e dei file. Dalla riga di comando, creare la struttura in questo modo:\n", "\n", "```bash\n", "mkdir my_project\n", @@ -577,7 +592,7 @@ "touch geometry.py\n", "```\n", "\n", - "A questo punto, devi inserire il codice nel file `geometry.py`:\n", + "A questo punto, inserire il codice nel file `geometry.py`:\n", "\n", "```python\n", "# content of operations/geometry.py file\n", @@ -585,24 +600,21 @@ " return base * height\n", "```\n", "\n", - "Infine, il codice per il file `main.py` sarà il seguente. `from operations.geometry import calcola_area_rettangolo` importa la funzione `calcola_area_rettangolo` dal modulo `geometry` che si trova all'interno del pacchetto `calcoli`.\n", + "Infine, il codice per il file `main.py` sarà il seguente:\n", "\n", "```python\n", "# content of main.py file\n", "from operations.geometry import calculate_rectangle_area\n", "\n", - "area = calculate_rectangle_area(10, 5)\n", - "print(f\"The area of the rectangle is: {area}\")\n", + "def rectangle_area():\n", + " area = calculate_rectangle_area(10, 5)\n", + " print(f\"The area of the rectangle is: {area}\")\n", + "\n", + "if __name__ == \"__main__\":\n", + " rectangle_area()\n", "```" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "metadata": {}, @@ -626,23 +638,17 @@ "def to_lowercase(text):\n", " return text.lower()\n", "\n", - "apply_format(\"Hello world\", to_uppercase)\n", - "apply_format(\"Hello world\", to_lowercase)" + "if __name__ == \"__main__\":\n", + " apply_format(\"Hello world\", to_uppercase)\n", + " apply_format(\"Hello world\", to_lowercase)" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "id": "footer", "metadata": {}, "source": [ - "© 2025 Hanamai. All rights reserved. | Built with precision for real-time data streaming excellence." + "© 2025 hanam.ai - All rights reserved. | Built with precision for real-time data streaming excellence." ] } ], diff --git a/06_fileio.ipynb b/06_fileio.ipynb index db6bf0c..59ccc40 100755 --- a/06_fileio.ipynb +++ b/06_fileio.ipynb @@ -316,27 +316,41 @@ "---" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Soluzioni\n", - "\n", - "### Soluzione Esercizio 1" - ] - }, + { + "cell_type": "markdown", + "id": "72c70c56", + "metadata": {}, + "source": [ + "---\n", + "## 💡 Soluzioni\n", + "\n", + "> 📂 **[Clicca qui per vedere il codice delle soluzioni](code/06/solutions)**\n", + "\n", + "---" + ] +}, + { + "cell_type": "markdown", + "id": "4895b613", + "metadata": {}, + "source": [ + "### Soluzione Esercizio 1" + ] + }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "names = [\"Mario\", \"Filippo\", \"Paolo\", \"Sofia\"]\n", - "\n", - "with open(\"names.txt\", \"w\") as file:\n", - " for name in names:\n", - " file.write(name + \"\\n\")" - ] + "def write_names():\n", + " with open(\"names.txt\", \"w\") as file:\n", + " for name in names:\n", + " file.write(name + \"\\n\")\n", + "\n", + "if __name__ == \"__main__\":\n", + " write_names()" + ] }, { "cell_type": "markdown", @@ -351,9 +365,13 @@ "metadata": {}, "outputs": [], "source": [ - "with open(\"names.txt\", \"r\") as file:\n", - " rows = file.readlines()\n", - " print(f\"File contains {len(rows)} names.\")" + "def read_names():\n", + " with open(\"names.txt\", \"r\") as file:\n", + " rows = file.readlines()\n", + " print(f\"File contains {len(rows)} names.\")\n", + "\n", + "if __name__ == \"__main__\":\n", + " read_names()" ] }, { @@ -361,7 +379,7 @@ "id": "footer", "metadata": {}, "source": [ - "© 2025 Hanamai. All rights reserved. | Built with precision for real-time data streaming excellence." + "© 2025 hanam.ai - All rights reserved. | Built with precision for real-time data streaming excellence." ] } ], diff --git a/07_libreria_standard.ipynb b/07_libreria_standard.ipynb index 8cc490c..472128e 100755 --- a/07_libreria_standard.ipynb +++ b/07_libreria_standard.ipynb @@ -275,13 +275,24 @@ "---" ] }, + { + "cell_type": "markdown", + "id": "72c70c517", + "metadata": {}, + "source": [ + "---\n", + "## 💡 Soluzioni\n", + "\n", + "> 📂 **[Clicca qui per vedere il codice delle soluzioni](code/07/solutions)**\n", + "\n", + "---" + ] +}, { "cell_type": "markdown", - "id": "1bef9760", + "id": "4895b113", "metadata": {}, "source": [ - "## Soluzioni\n", - "\n", "### Soluzione Esercizio 1" ] }, @@ -294,8 +305,12 @@ "source": [ "from datetime import date\n", "\n", - "today_str = date.today()\n", - "print(today_str.strftime('%d-%m-%Y'))" + "def print_today():\n", + " today_str = date.today()\n", + " print(today_str.strftime('%d-%m-%Y'))\n", + "\n", + "if __name__ == \"__main__\":\n", + " print_today()" ] }, { @@ -315,9 +330,13 @@ "source": [ "import random\n", "\n", - "numbers = [10, 20, 30, 40, 50]\n", - "selected_number = random.choice(numbers)\n", - "print(f\"Random number is: {selected_number}\")" + "def random_number():\n", + " numbers = [10, 20, 30, 40, 50]\n", + " selected_number = random.choice(numbers)\n", + " print(f\"Random number is: {selected_number}\")\n", + "\n", + "if __name__ == \"__main__\":\n", + " random_number()" ] }, { @@ -335,18 +354,18 @@ "metadata": {}, "outputs": [], "source": [ - "text = \" Hello, python is a FANTASTIC language! \"\n", - "\n", - "# 1. Remove whitespaces\n", - "clean_text = text.strip()\n", - "\n", - "# 2. Replace a word\n", - "replaced_text = clean_text.replace(\"FANTASTIC\", \"incredible\")\n", - "\n", - "# 3. Convert lower case\n", - "final_text = replaced_text.lower()\n", - "\n", - "print(final_text) # Output: hello, python is a incredible language!" + "def format_text():\n", + " text = \" Hello, python is a FANTASTIC language! \"\n", + " # 1. Remove whitespaces\n", + " clean_text = text.strip()\n", + " # 2. Replace a word\n", + " replaced_text = clean_text.replace(\"FANTASTIC\", \"incredible\")\n", + " # 3. Convert lower case\n", + " final_text = replaced_text.lower()\n", + " print(final_text) # Output: hello, python is a incredible language!\n", + "\n", + "if __name__ == \"__main__\":\n", + " format_text()" ] }, { @@ -354,7 +373,7 @@ "id": "footer", "metadata": {}, "source": [ - "© 2025 Hanamai. All rights reserved. | Built with precision for real-time data streaming excellence." + "© 2025 hanam.ai - All rights reserved. | Built with precision for real-time data streaming excellence." ] } ], diff --git a/08_librerie.ipynb b/08_librerie.ipynb index 047634e..42852e1 100755 --- a/08_librerie.ipynb +++ b/08_librerie.ipynb @@ -449,14 +449,6 @@ "Creare un array NumPy di 10 numeri casuali interi tra 1 e 50. Trovare il valore massimo, minimo e la media di questi numeri." ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "b2d08f6a", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "id": "c8deea85", @@ -474,14 +466,6 @@ "Calcolare e stampare il prezzo totale per ogni prodotto (`Quantity * Price`)." ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "8fa3bbe5", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "id": "d12fa15e", @@ -491,14 +475,6 @@ "Utilizzando Matplotlib, creare un semplice grafico a barre che mostri le quantità di frutta del DataFrame dell'esercizio precedente. Sull'asse X ci siano i nomi dei frutti e sull'asse Y le quantità." ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "9bc57b04", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "id": "3021664f", @@ -525,21 +501,24 @@ "id": "3a0acb6d62a7b0cc" }, { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [], - "id": "105dca2563385430" - }, + "cell_type": "markdown", + "id": "72c70c58", + "metadata": {}, + "source": [ + "---\n", + "## 💡 Soluzioni\n", + "\n", + "> 📂 **[Clicca qui per vedere il codice delle soluzioni](code/08/solutions)**\n", + "\n", + "---" + ] +}, { "cell_type": "markdown", - "id": "353ced40", + "id": "4815b113", "metadata": {}, "source": [ - "## Soluzioni\n", - "\n", - "### Soluzione Esercizio 1: Calcoli con NumPy\n" + "### Soluzione Esercizio 1" ] }, { @@ -553,11 +532,15 @@ "\n", "import numpy as np\n", "\n", - "random_numbers = np.random.randint(1, 51, 10)\n", - "print(f\"Array: {random_numbers}\")\n", - "print(f\"Max Value: {random_numbers.max()}\")\n", - "print(f\"Min Value: {random_numbers.min()}\")\n", - "print(f\"Average: {random_numbers.mean():.2f}\")" + "def random_numbers():\n", + " random_numbers = np.random.randint(1, 51, 10)\n", + " print(f\"Array: {random_numbers}\")\n", + " print(f\"Max Value: {random_numbers.max()}\")\n", + " print(f\"Min Value: {random_numbers.min()}\")\n", + " print(f\"Average: {random_numbers.mean():.2f}\")\n", + "\n", + "if __name__ == \"__main__\":\n", + " random_numbers()" ] }, { @@ -575,14 +558,16 @@ "metadata": {}, "outputs": [], "source": [ - "# Install the library with: pip install pandas\n", + "import numpy as np\n", "\n", - "import pandas as pd\n", + "def inventory():\n", + " data = {'Product': ['Apples', 'Bananas', 'Oranges'], 'Quantity': [10, 5, 15], 'Price': [1.20, 0.80, 1.50]}\n", + " df = pd.DataFrame(data)\n", + " df['Total Price'] = df['Quantity'] * df['Price']\n", + " print(df)\n", "\n", - "data = {'Product': ['Apples', 'Bananas', 'Oranges'], 'Quantity': [10, 5, 15], 'Price': [1.20, 0.80, 1.50]}\n", - "df = pd.DataFrame(data)\n", - "df['Total Price'] = df['Quantity'] * df['Price']\n", - "print(df)" + "if __name__ == \"__main__\":\n", + " inventory()" ] }, { @@ -600,18 +585,19 @@ "metadata": {}, "outputs": [], "source": [ - "# Install the library with: pip install matplotlib\n", - "\n", "import matplotlib.pyplot as plt\n", "\n", - "products = ['Apples', 'Bananas', 'Oranges']\n", - "quantity = [10, 5, 15]\n", - "\n", - "plt.bar(products, quantity, color=['red', 'yellow', 'orange'])\n", - "plt.title('Fruits into the store')\n", - "plt.xlabel('Product')\n", - "plt.ylabel('Quantity')\n", - "plt.show()" + "def plot_products():\n", + " products = ['Apples', 'Bananas', 'Oranges']\n", + " quantity = [10, 5, 15]\n", + " plt.bar(products, quantity, color=['red', 'yellow', 'orange'])\n", + " plt.title('Fruits into the store')\n", + " plt.xlabel('Product')\n", + " plt.ylabel('Quantity')\n", + " plt.show()\n", + "\n", + "if __name__ == \"__main__\":\n", + " plot_products()" ] }, { @@ -630,15 +616,17 @@ "source": [ "import re\n", "\n", - "text = \"Contacts are: pyhton@hanam.ai, info@hanam.ai, support@hanam.ai\"\n", - "\n", - "# 1. Extarct all emails\n", - "emails = re.findall(r'[\\w\\.-]+@[\\w\\.-]+', text)\n", - "print(\"Email found:\", emails)\n", - "\n", - "# 2. Replace domains with ***.com\n", - "masked_text = re.sub(r'@[\\w\\.-]+', '@***.ai', text)\n", - "print(\"New text:\", masked_text)" + "def extract_info():\n", + " text = \"Contacts are: pyhton@hanam.ai, info@hanam.ai, support@hanam.ai\"\n", + " # 1. Extract all emails\n", + " emails = re.findall(r'[\\w\\.-]+@[\\w\\.-]+', text)\n", + " print(\"Email found:\", emails)\n", + " # 2. Replace domains with ***.com\n", + " masked_text = re.sub(r'@[\\w\\.-]+', '@***.ai', text)\n", + " print(\"New text:\", masked_text)\n", + "\n", + "if __name__ == \"__main__\":\n", + " extract_info()" ], "id": "b2e90134179b6698" }, @@ -647,7 +635,7 @@ "id": "footer", "metadata": {}, "source": [ - "© 2025 Hanamai. All rights reserved. | Built with precision for real-time data streaming excellence." + "© 2025 hanam.ai - All rights reserved. | Built with precision for real-time data streaming excellence." ] } ], diff --git a/09_oop.ipynb b/09_oop.ipynb index a276ad5..c6e73df 100755 --- a/09_oop.ipynb +++ b/09_oop.ipynb @@ -457,21 +457,23 @@ ] }, { - "cell_type": "code", - "execution_count": null, - "id": "6acfbca7", - "metadata": {}, - "outputs": [], - "source": [] - }, + "cell_type": "markdown", + "id": "72c70c59", + "metadata": {}, + "source": [ + "---\n", + "## 💡 Soluzioni\n", + "\n", + "> 📂 **[Clicca qui per vedere il codice delle soluzioni](code/09/solutions)**\n", + "\n", + "---" + ] +}, { "cell_type": "markdown", - "id": "6e1a6202", + "id": "4815b213", "metadata": {}, "source": [ - "---\n", - "## Soluzioni\n", - "\n", "### Soluzione Esercizio 1" ] }, @@ -487,8 +489,9 @@ " self.brand = brand\n", " self.model = model\n", "\n", - "car = Car(\"Ferrari\", \"Testarossa\")\n", - "print(f\"Brand: {car.brand}, Model: {car.model}\")" + "if __name__ == \"__main__\":\n", + " car = Car(\"Ferrari\", \"Testarossa\")\n", + " print(f\"Brand: {car.brand}, Model: {car.model}\")" ] }, { @@ -514,8 +517,9 @@ " def show_details(self):\n", " print(f\"I own a {self.brand} {self.model}.\")\n", "\n", - "car = Car(\"Ferrari\", \"Testarossa\")\n", - "car.show_details()" + "if __name__ == \"__main__\":\n", + " car = Car(\"Ferrari\", \"Testarossa\")\n", + " car.show_details()" ] }, { @@ -546,7 +550,13 @@ "\n", "class Cow(Animal):\n", " def sound(self):\n", - " print(\"Muuu!\")" + " print(\"Muuu!\")\n", + "\n", + "if __name__ == \"__main__\":\n", + " lion = Lion()\n", + " cow = Cow()\n", + " lion.sound()\n", + " cow.sound()" ] }, { @@ -579,10 +589,10 @@ " def sound(self):\n", " print(\"Muuu!\")\n", "\n", - "animals = [Lion(), Cow()]\n", - "\n", - "for animal in animals:\n", - " animal.sound()" + "if __name__ == \"__main__\":\n", + " animals = [Lion(), Cow()]\n", + " for animal in animals:\n", + " animal.sound()" ] }, { @@ -603,13 +613,14 @@ " self.name = name\n", " self.species = species\n", "\n", - "elements = [10, \"hello\", Dog(\"Fido\", \"Labrador\"), 50.5]\n", + "if __name__ == \"__main__\":\n", + " elements = [10, \"hello\", Dog(\"Fido\", \"Labrador\"), 50.5]\n", "\n", - "for element in elements:\n", - " if isinstance(element, Dog):\n", - " print(f\"Found a dog! {element.name}.\")\n", - " else:\n", - " print(f\"It is not a dog. It is a {type(element)}.\")" + " for element in elements:\n", + " if isinstance(element, Dog):\n", + " print(f\"Found a dog! {element.name}.\")\n", + " else:\n", + " print(f\"It is not a dog. It is a {type(element)}.\")" ] }, { @@ -641,10 +652,11 @@ " else:\n", " print(\"Discount not valid. Enter a value between 0 and 100.\")\n", "\n", - "p = Product(200)\n", - "print(f\"Initial Prices: {p.get_price()}\")\n", - "p.set_discount(10)\n", - "p.set_discount(150)" + "if __name__ == \"__main__\":\n", + " p = Product(200)\n", + " print(f\"Initial Prices: {p.get_price()}\")\n", + " p.set_discount(10)\n", + " p.set_discount(150)" ] }, { @@ -652,7 +664,7 @@ "id": "footer", "metadata": {}, "source": [ - "© 2025 Hanamai. All rights reserved. | Built with precision for real-time data streaming excellence." + "© 2025 hanam.ai - All rights reserved. | Built with precision for real-time data streaming excellence." ] } ], diff --git a/10_testunit.ipynb b/10_testunit.ipynb index 492d741..20feae2 100755 --- a/10_testunit.ipynb +++ b/10_testunit.ipynb @@ -101,7 +101,7 @@ "source": [ "---", "\n", - "## Esercizi 📝\n", + "## Esercizi\n", "\n", "Scrivere i test unitari per le seguenti funzioni e classi. Creare un file di test separato per ogni esercizio." ] @@ -168,16 +168,19 @@ "4. Verificare che l'inizializzazione della classe sollevi un `ValueError` se `name` o `email` sono stringhe vuote." ] }, - { - "cell_type": "markdown", - "id": "be724524", - "metadata": {}, - "source": [ - "---", - "\n", - "## Soluzioni ✅" - ] - }, + { + "cell_type": "markdown", + "id": "72c70c61", + "metadata": {}, + "source": [ + "---\n", + "## 💡 Soluzioni\n", + "\n", + "> 📂 **[Clicca qui per vedere il codice delle soluzioni](code/10/solutions)**\n", + "\n", + "---" + ] +}, { "cell_type": "markdown", "id": "4955b271", @@ -197,7 +200,7 @@ "source": [ "# test_stats.py\n", "import unittest\n", - "from statistiche import calculate_average\n", + "from stats.stats import calculate_average\n", "\n", "class TestStats(unittest.TestCase):\n", " \n", @@ -237,7 +240,7 @@ "source": [ "# test_users.py\n", "import unittest\n", - "from utenti import User\n", + "from util.users import User\n", "\n", "class TestUser(unittest.TestCase):\n", " \n", @@ -276,7 +279,7 @@ "id": "footer", "metadata": {}, "source": [ - "© 2025 Hanamai. All rights reserved. | Built with precision for real-time data streaming excellence." + "© 2025 hanam.ai - All rights reserved. | Built with precision for real-time data streaming excellence." ] } ], diff --git a/11_decorator_closure.ipynb b/11_decorator_closure.ipynb old mode 100644 new mode 100755 index ec91646..44730b7 --- a/11_decorator_closure.ipynb +++ b/11_decorator_closure.ipynb @@ -260,14 +260,26 @@ "\n" ] }, + { + "cell_type": "markdown", + "id": "72c70c62", + "metadata": {}, + "source": [ + "---\n", + "## 💡 Soluzioni\n", + "\n", + "> 📂 **[Clicca qui per vedere il codice delle soluzioni](code/11/solutions)**\n", + "\n", + "---" + ] +}, { "cell_type": "markdown", + "id": "4815b213", "metadata": {}, "source": [ - "## Soluzioni\n", - "\n", - "### Soluzione Esercizio 1" - ] + "### Soluzione Esercizio 1" + ] }, { "cell_type": "code", @@ -286,7 +298,8 @@ "def add(a, b):\n", " return a + b\n", "\n", - "print(add(5, 3))" + "if __name__ == \"__main__\":\n", + " print(add(5, 3))" ] }, { @@ -319,7 +332,8 @@ " time.sleep(1)\n", " print(\"Finished!\")\n", "\n", - "slow_function()" + "if __name__ == \"__main__\":\n", + " slow_function()" ] }, { @@ -349,7 +363,8 @@ "def say_hi():\n", " print(\"Hi!\")\n", "\n", - "say_hi()" + "if __name__ == \"__main__\":\n", + " say_hi()" ] }, { @@ -357,7 +372,7 @@ "id": "footer", "metadata": {}, "source": [ - "© 2025 Hanamai. All rights reserved. | Built with precision for real-time data streaming excellence." + "© 2025 hanam.ai - All rights reserved. | Built with precision for real-time data streaming excellence." ] } ], diff --git a/12_progetto.ipynb b/12_progetto.ipynb index b0015bc..3befc1c 100755 --- a/12_progetto.ipynb +++ b/12_progetto.ipynb @@ -24,6 +24,15 @@ "- **Input/Output da file** con il modulo `json` per salvare e caricare i dati in modo persistente.\n", "- **Uso di un decorator** che ogni volta che viene eseguita un’azione (aggiunta, vendita, salvataggio, caricamento), registra l'azione file actions.log" ]}, + { + "cell_type": "markdown", + "id": "72c70c63", + "metadata": {}, + "source": [ + "\n", + "> 📂 **[Clicca qui per vedere il codice del progetto](code/12/solutions)**\n" + ] +}, { "cell_type": "markdown", "id": "0a7aa72a", @@ -67,8 +76,7 @@ "├── main.py                 # Loop principale e interfaccia utente\n", "├── models.py               # Classi per la modellazione dei prodotti\n", "├── inventory_manager.py    # Classe per la gestione dell'inventario\n", - "└── tests/                  # Cartella per i test unitari\n", - "    └── test_inventory.py   # File di test per la logica del programma\n", + "└── test_inventory.py   # File di test per la logica del programma\n", "```\n", "\n", "### File `requirements.txt`\n", @@ -159,7 +167,7 @@ "\n", "\n", "# Subclass for food products\n", - "class FoodItem(BaseItem:\n", + "class FoodItem(BaseItem):\n", " def __init__(self, name: str, price: float, quantity: int, expiration_date: str):\n", " super().__init__(name, price, quantity)\n", " self.expiration_date = expiration_date\n", @@ -261,7 +269,7 @@ "\n", " @log_action\n", " def add_item(self, item):\n", - " if item.name in self.load_inventory:\n", + " if item.name in self.inventory:\n", " print(f\"{Fore.YELLOW}Warning: Item {item.name} already exists.{Style.RESET_ALL}\")\n", " return False\n", " else:\n", @@ -307,7 +315,7 @@ "\n", " def load_inventory(self):\n", " try:\n", - " with open(self.NOME_FILE, 'r') as f:\n", + " with open(self.FILE_NAME, 'r') as f:\n", " inventory_loaded = json.load(f)\n", " self.inventory = {}\n", " for name, item_details in inventory_loaded.items():\n", @@ -562,7 +570,7 @@ "metadata": {}, "outputs": [], "source": [ - "python -m unittest tests/test_inventory.py" + "python -m unittest test_inventory.py" ] }, { @@ -570,7 +578,7 @@ "id": "footer", "metadata": {}, "source": [ - "© 2025 Hanamai. All rights reserved. | Built with precision for real-time data streaming excellence." + "© 2025 hanam.ai - All rights reserved. | Built with precision for real-time data streaming excellence." ] } ], diff --git a/README.md b/README.md index 342bfeb..e66630d 100755 --- a/README.md +++ b/README.md @@ -203,4 +203,10 @@ Per approfondire come si usano i notebook interattivi e Jupyter, ecco alcuni lin --- -© 2025 Hanamai. All rights reserved. | Built with precision for real-time data streaming excellence. +## 💻 Codice e soluzioni agli esercizi + +Tutte le soluzioni agli esercizi proposti nei vari capitoli sono disponibili nella cartella [_**code**_ ](code/) del repository e suddivisi per i vari capitoli. + +--- + +© 2025 hanam.ai - All rights reserved. | Built with precision for real-time data streaming excellence. diff --git a/appendix.ipynb b/appendix.ipynb index 7445784..2468b53 100755 --- a/appendix.ipynb +++ b/appendix.ipynb @@ -101,7 +101,7 @@ "id": "footer", "metadata": {}, "source": [ - "© 2025 Hanamai. All rights reserved. | Built with precision for real-time data streaming excellence." + "© 2025 hanam.ai - All rights reserved. | Built with precision for real-time data streaming excellence." ] } ], diff --git a/code/01/solutions/exercise01.py b/code/01/solutions/exercise01.py new file mode 100755 index 0000000..a62c856 --- /dev/null +++ b/code/01/solutions/exercise01.py @@ -0,0 +1,6 @@ +def greet_user(): + user_name = input("What is your name? ") + print(f"Hello {user_name}! It is a real pleasure to have you here.") + +if __name__ == "__main__": + greet_user() \ No newline at end of file diff --git a/code/01/solutions/exercise02.py b/code/01/solutions/exercise02.py new file mode 100755 index 0000000..8d76c44 --- /dev/null +++ b/code/01/solutions/exercise02.py @@ -0,0 +1,9 @@ +import math + +def calculate_square_root(): + number = float(input("Enter a number: ")) + square_root = math.sqrt(number) + print(f"The square root of {number} is {square_root}") + +if __name__ == "__main__": + calculate_square_root() \ No newline at end of file diff --git a/code/01/solutions/exercise03.py b/code/01/solutions/exercise03.py new file mode 100755 index 0000000..ce6a11e --- /dev/null +++ b/code/01/solutions/exercise03.py @@ -0,0 +1,7 @@ +def sum(a, b): + """Return the sum of two numbers.""" + return a + b + +if __name__ == "__main__": + result = sum(5, 3) + print(f"Result is: {result}") \ No newline at end of file diff --git a/code/02/solutions/exercise01.py b/code/02/solutions/exercise01.py new file mode 100755 index 0000000..b2f1ebf --- /dev/null +++ b/code/02/solutions/exercise01.py @@ -0,0 +1,7 @@ +def greet_user(): + user_name = "Luca" + age = 25 + print(f"Hi, my name is {user_name} and I am {age} years old.") + +if __name__ == "__main__": + greet_user() \ No newline at end of file diff --git a/code/02/solutions/exercise02.py b/code/02/solutions/exercise02.py new file mode 100755 index 0000000..f99344d --- /dev/null +++ b/code/02/solutions/exercise02.py @@ -0,0 +1,13 @@ +def guess_numbers(): + a = int(input("First number: ")) + b = int(input("Second number: ")) + if a > b: + print("First number is greater.") + elif a < b: + print("First number is less.") + else: + print("Numbers are equal.") + print(f"Hi, my name is {user_name} and I am {age} years old.") + +if __name__ == "__main__": + guess_numbers() \ No newline at end of file diff --git a/code/02/solutions/exercise03.py b/code/02/solutions/exercise03.py new file mode 100755 index 0000000..66aaae1 --- /dev/null +++ b/code/02/solutions/exercise03.py @@ -0,0 +1,11 @@ +def ask_grade(): + grade = int(input("Enter a grade: ")) + if grade >= 60: + print("Passed") + elif grade >= 40: + print("Remedial") + else: + print("Failed") + +if __name__ == "__main__": + ask_grade() \ No newline at end of file diff --git a/code/02/solutions/exercise04.py b/code/02/solutions/exercise04.py new file mode 100755 index 0000000..0ed7dcb --- /dev/null +++ b/code/02/solutions/exercise04.py @@ -0,0 +1,6 @@ +def print_numbers(): + for i in range(1, 11): + print(i) + +if __name__ == "__main__": + print_numbers() \ No newline at end of file diff --git a/code/02/solutions/exercise05.py b/code/02/solutions/exercise05.py new file mode 100755 index 0000000..eaec552 --- /dev/null +++ b/code/02/solutions/exercise05.py @@ -0,0 +1,13 @@ +def guess_number(): + secret_number = 7 + while True: + guess = int(input("Guess a number ")) + print(guess) + if guess == secret_number: + print("You guessed it!") + break + else: + print("Try again.") + +if __name__ == "__main__": + guess_number() \ No newline at end of file diff --git a/code/03/solutions/exercise01.py b/code/03/solutions/exercise01.py new file mode 100755 index 0000000..eaa8e26 --- /dev/null +++ b/code/03/solutions/exercise01.py @@ -0,0 +1,11 @@ +def details(): + colours = ['yellow', 'orange', 'violet'] + coordinates = (45, 90) + person = {'name': 'Paolo', 'city': 'Milano'} + + print(colours[1]) + print(coordinates[-1]) + print(person['name']) + +if __name__ == "__main__": + details() \ No newline at end of file diff --git a/code/03/solutions/exercise02.py b/code/03/solutions/exercise02.py new file mode 100755 index 0000000..35c42b3 --- /dev/null +++ b/code/03/solutions/exercise02.py @@ -0,0 +1,8 @@ +def join_url(): + url_parts = ["https:", "", "www.python.org", "doc"] + final_url = "//".join(url_parts[:2]) + "/".join(url_parts[2:]) + + print(final_url) + +if __name__ == "__main__": + join_url() \ No newline at end of file diff --git a/code/03/solutions/exercise03.py b/code/03/solutions/exercise03.py new file mode 100755 index 0000000..0c22867 --- /dev/null +++ b/code/03/solutions/exercise03.py @@ -0,0 +1,7 @@ +def add_number(): + numbers = [4, 1, 7, 3, 9] + numbers.append(6) + print(sorted(numbers)) + +if __name__ == "__main__": + add_number() \ No newline at end of file diff --git a/code/03/solutions/exercise04.py b/code/03/solutions/exercise04.py new file mode 100755 index 0000000..175b16e --- /dev/null +++ b/code/03/solutions/exercise04.py @@ -0,0 +1,6 @@ +def coordinates(): + coordinates = (12.5, 8.2) + print(coordinates[0]) + +if __name__ == "__main__": + coordinates() \ No newline at end of file diff --git a/code/03/solutions/exercise05.py b/code/03/solutions/exercise05.py new file mode 100755 index 0000000..ba8e8a2 --- /dev/null +++ b/code/03/solutions/exercise05.py @@ -0,0 +1,7 @@ +def animals(): + animals = {"cat", "dog"} + animals.add("fox") + print(animals) + +if __name__ == "__main__": + animals() \ No newline at end of file diff --git a/code/03/solutions/exercise06.py b/code/03/solutions/exercise06.py new file mode 100755 index 0000000..51e8083 --- /dev/null +++ b/code/03/solutions/exercise06.py @@ -0,0 +1,6 @@ +def dict_student(): + student = {"name": "Mario", "age": 21, "course": "Relational Databases"} + print(*student.values()) # Python 3 .values, it returns a view object, not a list, unpack the object + +if __name__ == "__main__": + dict_student() \ No newline at end of file diff --git a/code/03/solutions/exercise07.py b/code/03/solutions/exercise07.py new file mode 100755 index 0000000..8e348f3 --- /dev/null +++ b/code/03/solutions/exercise07.py @@ -0,0 +1,17 @@ +def iter_colours(): + colours = ['red', 'green', 'blue'] + + # obtain the iterator from the list + iter_colours = iter(colours) + + # Simulate a for loop using a while loop with try-except. + while True: + try: + colour = next(iter_colours) + print(colour) + except StopIteration: + # The iterator has exhausted its elements, so we exit the loop. + break + +if __name__ == "__main__": + iter_colours() \ No newline at end of file diff --git a/code/03/solutions/exercise08.py b/code/03/solutions/exercise08.py new file mode 100755 index 0000000..f23e9bc --- /dev/null +++ b/code/03/solutions/exercise08.py @@ -0,0 +1,7 @@ +def sum_three_numbers(a, b, c): + return a + b + c + +if __name__ == "__main__": + numbers = [10, 20, 30] + result = sum_three_numbers(*numbers) + print(f"Sum is: {result}") \ No newline at end of file diff --git a/code/03/solutions/exercise09.py b/code/03/solutions/exercise09.py new file mode 100755 index 0000000..e582954 --- /dev/null +++ b/code/03/solutions/exercise09.py @@ -0,0 +1,6 @@ +def save_user(username, password): + print(f"User {username} saved with password {password}!") + +if __name__ == "__main__": + credentials = {'username': 'admin', 'password': 'password'} + save_user(**credentials) \ No newline at end of file diff --git a/code/03/solutions/exercise10.py b/code/03/solutions/exercise10.py new file mode 100755 index 0000000..a051299 --- /dev/null +++ b/code/03/solutions/exercise10.py @@ -0,0 +1,6 @@ +def squares(): + squares = [x**2 for x in range(1, 11)] + print(squares) + +if __name__ == "__main__": + squares() \ No newline at end of file diff --git a/code/03/solutions/exercise11.py b/code/03/solutions/exercise11.py new file mode 100755 index 0000000..a58b8bd --- /dev/null +++ b/code/03/solutions/exercise11.py @@ -0,0 +1,7 @@ +def words_length(): + words = ['python', 'ai', 'data'] + lengths = {word: len(word) for word in words} + print(lengths) + +if __name__ == "__main__": + words_length() \ No newline at end of file diff --git a/code/03/solutions/exercise12.py b/code/03/solutions/exercise12.py new file mode 100755 index 0000000..65a7ffc --- /dev/null +++ b/code/03/solutions/exercise12.py @@ -0,0 +1,7 @@ +def first_letter(): + words = ['python', 'ai', 'data'] + first_letters = {word[0] for word in words} + print(first_letters) + +if __name__ == "__main__": + first_letter() \ No newline at end of file diff --git a/code/03/solutions/exercise13.py b/code/03/solutions/exercise13.py new file mode 100755 index 0000000..f9c3eba --- /dev/null +++ b/code/03/solutions/exercise13.py @@ -0,0 +1,7 @@ +def gen_numbers(): + gen = (x**3 for x in range(5)) + for value in gen: + print(value) + +if __name__ == "__main__": + gen_numbers() \ No newline at end of file diff --git a/code/04/solutions/exercise01.py b/code/04/solutions/exercise01.py new file mode 100755 index 0000000..1d85bc3 --- /dev/null +++ b/code/04/solutions/exercise01.py @@ -0,0 +1,13 @@ +def divide_numbers(): + try: + a = float(input("Enter first number: ")) + b = float(input("Enter second number: ")) + result = a / b + print(f"Division result is: {result}") + except ZeroDivisionError: + print("Error: Division by zero.") + except ValueError: + print("Error: input not valid. Enter only numbers.") + +if __name__ == "__main__": + divide_numbers() \ No newline at end of file diff --git a/code/04/solutions/exercise02.py b/code/04/solutions/exercise02.py new file mode 100755 index 0000000..ef9f5ff --- /dev/null +++ b/code/04/solutions/exercise02.py @@ -0,0 +1,12 @@ +def index_fruits(): + fruits = ['apple', 'banana', 'kiwi'] + try: + index = int(input(f"Enter an index (0-{len(fruits)-1}): ")) + print(f"Element at index {index} is: {fruits[index]}") + except ValueError: + print("Error: Input is not an int.") + except IndexError: + print(f"Error: Index out of range (0-{len(fruits)-1}).") + +if __name__ == "__main__": + index_fruits() \ No newline at end of file diff --git a/code/04/solutions/exercise03.py b/code/04/solutions/exercise03.py new file mode 100755 index 0000000..9c01305 --- /dev/null +++ b/code/04/solutions/exercise03.py @@ -0,0 +1,10 @@ +def convert_int(): + try: + number_str = input("Enter an int: ") + number_int = int(number_str) + print(f"Int is: {number_int}") + except ValueError: + print(f"Error: '{number_str}' is not a valid int.") + +if __name__ == "__main__": + convert_int() \ No newline at end of file diff --git a/code/05/solutions/exercise01.py b/code/05/solutions/exercise01.py new file mode 100755 index 0000000..e2f8f92 --- /dev/null +++ b/code/05/solutions/exercise01.py @@ -0,0 +1,6 @@ +def calculate_rectangle_area(base, height): + return base * height + +if __name__ == "__main__": + area = calculate_rectangle_area(10, 5) + print(f"The area of the rectangle is: {area}") \ No newline at end of file diff --git a/code/05/solutions/exercise02.py b/code/05/solutions/exercise02.py new file mode 100755 index 0000000..6d15acf --- /dev/null +++ b/code/05/solutions/exercise02.py @@ -0,0 +1,6 @@ +def greet(name, message='Hello'): + print(f"{message}, {name}!") + +if __name__ == "__main__": + greet("Mario") + greet("Ugo", "Good morning") \ No newline at end of file diff --git a/code/05/solutions/exercise03.py b/code/05/solutions/exercise03.py new file mode 100755 index 0000000..c5cdc3f --- /dev/null +++ b/code/05/solutions/exercise03.py @@ -0,0 +1,8 @@ +import datetime + +def current_time(): + current_time = datetime.datetime.now() + print(f"The current date and time are: {current_time}") + +if __name__ == "__main__": + current_time() \ No newline at end of file diff --git a/code/05/solutions/exercise05.py b/code/05/solutions/exercise05.py new file mode 100755 index 0000000..1774fe2 --- /dev/null +++ b/code/05/solutions/exercise05.py @@ -0,0 +1,14 @@ +def apply_format(str, formatter): + result = formatter(str) + print(f"The formatted string is: {result}") + +def to_uppercase(text): + return text.upper() + +def to_lowercase(text): + return text.lower() + + +if __name__ == "__main__": + apply_format("Hello world", to_uppercase) + apply_format("Hello world", to_lowercase) \ No newline at end of file diff --git a/code/05/solutions/exercise4/main.py b/code/05/solutions/exercise4/main.py new file mode 100755 index 0000000..dc5ac57 --- /dev/null +++ b/code/05/solutions/exercise4/main.py @@ -0,0 +1,9 @@ +# content of main.py file +from operations.geometry import calculate_rectangle_area + +def rectangle_area(): + area = calculate_rectangle_area(10, 5) + print(f"The area of the rectangle is: {area}") + +if __name__ == "__main__": + rectangle_area() \ No newline at end of file diff --git a/code/05/solutions/exercise4/operations/__init__.py b/code/05/solutions/exercise4/operations/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/code/05/solutions/exercise4/operations/geometry.py b/code/05/solutions/exercise4/operations/geometry.py new file mode 100755 index 0000000..5a0ea76 --- /dev/null +++ b/code/05/solutions/exercise4/operations/geometry.py @@ -0,0 +1,3 @@ +# content of operations/geometry.py file +def calculate_rectangle_area(base, height): + return base * height \ No newline at end of file diff --git a/code/06/solutions/exercise01.py b/code/06/solutions/exercise01.py new file mode 100755 index 0000000..b6e9ca8 --- /dev/null +++ b/code/06/solutions/exercise01.py @@ -0,0 +1,9 @@ +def write_names(): + names = ["Mario", "Filippo", "Paolo", "Sofia"] + + with open("names.txt", "w") as file: + for name in names: + file.write(name + "\n") + +if __name__ == "__main__": + write_names() \ No newline at end of file diff --git a/code/06/solutions/exercise02.py b/code/06/solutions/exercise02.py new file mode 100755 index 0000000..a3d0cdd --- /dev/null +++ b/code/06/solutions/exercise02.py @@ -0,0 +1,7 @@ +def read_names(): + with open("names.txt", "r") as file: + rows = file.readlines() + print(f"File contains {len(rows)} names.") + +if __name__ == "__main__": + read_names() \ No newline at end of file diff --git a/code/06/solutions/names.txt b/code/06/solutions/names.txt new file mode 100755 index 0000000..880fdb3 --- /dev/null +++ b/code/06/solutions/names.txt @@ -0,0 +1,4 @@ +Mario +Filippo +Paolo +Sofia diff --git a/code/07/solutions/exercise01.py b/code/07/solutions/exercise01.py new file mode 100755 index 0000000..bc38851 --- /dev/null +++ b/code/07/solutions/exercise01.py @@ -0,0 +1,8 @@ +from datetime import date + +def print_today(): + today_str = date.today() + print(today_str.strftime('%d-%m-%Y')) + +if __name__ == "__main__": + print_today() \ No newline at end of file diff --git a/code/07/solutions/exercise02.py b/code/07/solutions/exercise02.py new file mode 100755 index 0000000..614a8b0 --- /dev/null +++ b/code/07/solutions/exercise02.py @@ -0,0 +1,9 @@ +import random + +def random_number(): + numbers = [10, 20, 30, 40, 50] + selected_number = random.choice(numbers) + print(f"Random number is: {selected_number}") + +if __name__ == "__main__": + random_number() \ No newline at end of file diff --git a/code/07/solutions/exercise03.py b/code/07/solutions/exercise03.py new file mode 100755 index 0000000..4596a49 --- /dev/null +++ b/code/07/solutions/exercise03.py @@ -0,0 +1,12 @@ +def format_text(): + text = " Hello, python is a FANTASTIC language! " + # 1. Remove whitespaces + clean_text = text.strip() + # 2. Replace a word + replaced_text = clean_text.replace("FANTASTIC", "incredible") + # 3. Convert lower case + final_text = replaced_text.lower() + print(final_text) # Output: hello, python is an incredible language! + +if __name__ == "__main__": + format_text() \ No newline at end of file diff --git a/code/08/solutions/exercise01.py b/code/08/solutions/exercise01.py new file mode 100755 index 0000000..b152cd8 --- /dev/null +++ b/code/08/solutions/exercise01.py @@ -0,0 +1,11 @@ +import numpy as np + +def random_numbers(): + random_numbers = np.random.randint(1, 51, 10) + print(f"Array: {random_numbers}") + print(f"Max Value: {random_numbers.max()}") + print(f"Min Value: {random_numbers.min()}") + print(f"Average: {random_numbers.mean():.2f}") + +if __name__ == "__main__": + random_numbers() \ No newline at end of file diff --git a/code/08/solutions/exercise02.py b/code/08/solutions/exercise02.py new file mode 100755 index 0000000..d4009d7 --- /dev/null +++ b/code/08/solutions/exercise02.py @@ -0,0 +1,10 @@ +import pandas as pd + +def inventory(): + data = {'Product': ['Apples', 'Bananas', 'Oranges'], 'Quantity': [10, 5, 15], 'Price': [1.20, 0.80, 1.50]} + df = pd.DataFrame(data) + df['Total Price'] = df['Quantity'] * df['Price'] + print(df) + +if __name__ == "__main__": + inventory() \ No newline at end of file diff --git a/code/08/solutions/exercise03.py b/code/08/solutions/exercise03.py new file mode 100755 index 0000000..ecf7ab7 --- /dev/null +++ b/code/08/solutions/exercise03.py @@ -0,0 +1,14 @@ +import matplotlib.pyplot as plt + +def plot_products(): + products = ['Apples', 'Bananas', 'Oranges'] + quantity = [10, 5, 15] + + plt.bar(products, quantity, color=['red', 'yellow', 'orange']) + plt.title('Fruits into the store') + plt.xlabel('Product') + plt.ylabel('Quantity') + plt.show() + +if __name__ == "__main__": + plot_products() \ No newline at end of file diff --git a/code/08/solutions/exercise04.py b/code/08/solutions/exercise04.py new file mode 100755 index 0000000..e1e9f8c --- /dev/null +++ b/code/08/solutions/exercise04.py @@ -0,0 +1,13 @@ +import re + +def extract_info(): + text = "Contacts are: pyhton@hanam.ai, info@hanam.ai, support@hanam.ai" + # 1. Extract all emails + emails = re.findall(r'[\w\.-]+@[\w\.-]+', text) + print("Email found:", emails) + # 2. Replace domains with ***.com + masked_text = re.sub(r'@[\w\.-]+', '@***.ai', text) + print("New text:", masked_text) + +if __name__ == "__main__": + extract_info() \ No newline at end of file diff --git a/code/09/solutions/exercise01.py b/code/09/solutions/exercise01.py new file mode 100755 index 0000000..41e38f8 --- /dev/null +++ b/code/09/solutions/exercise01.py @@ -0,0 +1,8 @@ +class Car: + def __init__(self, brand, model): + self.brand = brand + self.model = model + +if __name__ == "__main__": + car = Car("Ferrari", "Testarossa") + print(f"Brand: {car.brand}, Model: {car.model}") \ No newline at end of file diff --git a/code/09/solutions/exercise02.py b/code/09/solutions/exercise02.py new file mode 100755 index 0000000..bb3c533 --- /dev/null +++ b/code/09/solutions/exercise02.py @@ -0,0 +1,11 @@ +class Car: + def __init__(self, brand, model): + self.brand = brand + self.model = model + + def show_details(self): + print(f"I own a {self.brand} {self.model}.") + +if __name__ == "__main__": + car = Car("Ferrari", "Testarossa") + car.show_details() \ No newline at end of file diff --git a/code/09/solutions/exercise03.py b/code/09/solutions/exercise03.py new file mode 100755 index 0000000..e1818b2 --- /dev/null +++ b/code/09/solutions/exercise03.py @@ -0,0 +1,20 @@ +from abc import ABC, abstractmethod + +class Animal(ABC): + @abstractmethod + def sound(self): + pass + +class Lion(Animal): + def sound(self): + print("Roarrrr!") + +class Cow(Animal): + def sound(self): + print("Muuu!") + +if __name__ == "__main__": + lion = Lion() + cow = Cow() + lion.sound() + cow.sound() \ No newline at end of file diff --git a/code/09/solutions/exercise04.py b/code/09/solutions/exercise04.py new file mode 100755 index 0000000..0ff1075 --- /dev/null +++ b/code/09/solutions/exercise04.py @@ -0,0 +1,20 @@ +from abc import ABC, abstractmethod + +class Animal(ABC): + @abstractmethod + def sound(self): + pass + +class Lion(Animal): + def sound(self): + print("Roarrrr!") + +class Cow(Animal): + def sound(self): + print("Muuu!") + +if __name__ == "__main__": + animals = [Lion(), Cow()] + + for animal in animals: + animal.sound() \ No newline at end of file diff --git a/code/09/solutions/exercise05.py b/code/09/solutions/exercise05.py new file mode 100755 index 0000000..f6a0764 --- /dev/null +++ b/code/09/solutions/exercise05.py @@ -0,0 +1,13 @@ +class Dog: + def __init__(self, name, species): + self.name = name + self.species = species + +if __name__ == "__main__": + elements = [10, "hello", Dog("Fido", "Labrador"), 50.5] + + for element in elements: + if isinstance(element, Dog): + print(f"Found a dog! {element.name}.") + else: + print(f"It is not a dog. It is a {type(element)}.") \ No newline at end of file diff --git a/code/09/solutions/exercise06.py b/code/09/solutions/exercise06.py new file mode 100755 index 0000000..8d5fbef --- /dev/null +++ b/code/09/solutions/exercise06.py @@ -0,0 +1,19 @@ +class Product: + def __init__(self, price): + self.__price = price + + def get_price(self): + return self.__price + + def set_discount(self, discount): + if 0 <= discount <= 100: + self.__price = self.__price * (1 - discount / 100) + print(f"New pricew: {self.__price}") + else: + print("Discount not valid. Enter a value between 0 and 100.") + +if __name__ == "__main__": + p = Product(200) + print(f"Initial Prices: {p.get_price()}") + p.set_discount(10) + p.set_discount(150) \ No newline at end of file diff --git a/code/10/solutions/exercise1/stats/__init__.py b/code/10/solutions/exercise1/stats/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/code/10/solutions/exercise1/stats/stats.py b/code/10/solutions/exercise1/stats/stats.py new file mode 100755 index 0000000..95ff40a --- /dev/null +++ b/code/10/solutions/exercise1/stats/stats.py @@ -0,0 +1,5 @@ +def calculate_average(list_numbers): + """Calculates the average of a list of numbers.""" + if not list_numbers: + raise ValueError("Impossible to calculate the average of an empty list.") + return sum(list_numbers) / len(list_numbers) \ No newline at end of file diff --git a/code/10/solutions/exercise1/test_stats.py b/code/10/solutions/exercise1/test_stats.py new file mode 100755 index 0000000..49586ec --- /dev/null +++ b/code/10/solutions/exercise1/test_stats.py @@ -0,0 +1,20 @@ +import unittest +from stats.stats import calculate_average + +class TestStats(unittest.TestCase): + + def test_average_on_ints(self): + self.assertEqual(calculate_average([1, 2, 3, 4, 5]), 3.0) + + def test_average_on_floats(self): + self.assertEqual(calculate_average([1.5, 2.5, 3.5]), 2.5) + + def test_empty_list(self): + with self.assertRaises(ValueError): + calculate_average([]) + + def test_list_with_single_element(self): + self.assertEqual(calculate_average([10]), 10.0) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/code/10/solutions/exercise2/test_users.py b/code/10/solutions/exercise2/test_users.py new file mode 100755 index 0000000..8a7cd28 --- /dev/null +++ b/code/10/solutions/exercise2/test_users.py @@ -0,0 +1,33 @@ +import unittest +from util.users import User + +class TestUser(unittest.TestCase): + + def setUp(self): + # This method runs before each test. + # It is useful to create a clean instance of the class to be tested. + self.valid_user = User("Mario Rossi", "mario.rossi@email.com") + + def test_init_ok(self): + self.assertEqual(self.valid_user.name, "Mario Rossi") + self.assertEqual(self.valid_user.email, "mario.rossi@email.com") + self.assertFalse(self.valid_user.logged) + + def test_login(self): + self.valid_user.login() + self.assertTrue(self.valid_user.logged) + + def test_logout(self): + self.valid_user.login() + self.valid_user.logout() + self.assertFalse(self.valid_user.logged) + + def test_init_empty_values(self): + with self.assertRaises(ValueError): + User("", "test@email.com") + + with self.assertRaises(ValueError): + User("Name", "") + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/code/10/solutions/exercise2/util/__init__.py b/code/10/solutions/exercise2/util/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/code/10/solutions/exercise2/util/users.py b/code/10/solutions/exercise2/util/users.py new file mode 100755 index 0000000..868c6a6 --- /dev/null +++ b/code/10/solutions/exercise2/util/users.py @@ -0,0 +1,13 @@ +class User: + def __init__(self, name, email): + if not name or not email: + raise ValueError("Name and email must be not empty.") + self.name = name + self.email = email + self.logged = False + + def login(self): + self.logged = True + + def logout(self): + self.logged = False \ No newline at end of file diff --git a/code/11/solutions/exercise01.py b/code/11/solutions/exercise01.py new file mode 100755 index 0000000..79b07fd --- /dev/null +++ b/code/11/solutions/exercise01.py @@ -0,0 +1,13 @@ +# Logging Decorator +def log_args(func): + def wrapper(*args, **kwargs): + print(f"Arguments: {args}, Keyword Arguments: {kwargs}") + return func(*args, **kwargs) + return wrapper + +@log_args +def add(a, b): + return a + b + +if __name__ == "__main__": + print(add(5, 3)) \ No newline at end of file diff --git a/code/11/solutions/exercise02.py b/code/11/solutions/exercise02.py new file mode 100755 index 0000000..ad29f09 --- /dev/null +++ b/code/11/solutions/exercise02.py @@ -0,0 +1,19 @@ +# Timer Decorator +import time + +def timer(func): + def wrapper(*args, **kwargs): + start = time.time() + result = func(*args, **kwargs) + end = time.time() + print(f"Execution time: {end - start:.4f} seconds") + return result + return wrapper + +@timer +def slow_function(): + time.sleep(1) + print("Finished!") + +if __name__ == "__main__": + slow_function() \ No newline at end of file diff --git a/code/11/solutions/exercise03.py b/code/11/solutions/exercise03.py new file mode 100755 index 0000000..f5e9685 --- /dev/null +++ b/code/11/solutions/exercise03.py @@ -0,0 +1,16 @@ +# Repeat Decorator with parameter +def repeat(n): + def decorator(func): + def wrapper(*args, **kwargs): + for i in range(n): + print(f"Execution {i+1}/{n}") + func(*args, **kwargs) + return wrapper + return decorator + +@repeat(3) +def say_hi(): + print("Hi!") + +if __name__ == "__main__": + say_hi() \ No newline at end of file diff --git a/code/12/solutions/inventory-manager/actions.log b/code/12/solutions/inventory-manager/actions.log new file mode 100755 index 0000000..21f4b03 --- /dev/null +++ b/code/12/solutions/inventory-manager/actions.log @@ -0,0 +1 @@ +add_item called with args=(,), kwargs={} diff --git a/code/12/solutions/inventory-manager/inventory.json b/code/12/solutions/inventory-manager/inventory.json new file mode 100755 index 0000000..f495db1 --- /dev/null +++ b/code/12/solutions/inventory-manager/inventory.json @@ -0,0 +1,9 @@ +{ + "biscuits": { + "name": "biscuits", + "price": 121.0, + "quantity": 212, + "expiration_date": "12-12-2030", + "item_type": "FoodItem" + } +} \ No newline at end of file diff --git a/code/12/solutions/inventory-manager/inventory_manager.py b/code/12/solutions/inventory-manager/inventory_manager.py new file mode 100755 index 0000000..2560b5e --- /dev/null +++ b/code/12/solutions/inventory-manager/inventory_manager.py @@ -0,0 +1,86 @@ +import json +from colorama import Fore, Style +from models import Item, FoodItem, ElectronicItem +from functools import wraps + +# Decorator to log actions +def log_action(func): + @wraps(func) + def wrapper(*args, **kwargs): + result = func(*args, **kwargs) + with open('actions.log', 'a') as f: + f.write(f'{func.__name__} called with args={args[1:]}, kwargs={kwargs}\n') + return result + return wrapper + +class InventoryManager: + """Class that manages all operations on the inventory.""" + FILE_NAME = "inventory.json" + + def __init__(self): + self.inventory = {} + self.load_inventory() + + @log_action + def add_item(self, item): + if item.name in self.inventory: + print(f"{Fore.YELLOW}Warning: Item {item.name} already exists.{Style.RESET_ALL}") + return False + else: + self.inventory[item.name] = item + print(f"{Fore.GREEN}Item {item.name} added.{Style.RESET_ALL}") + return True + + @log_action + def sell_item(self, name: str, sold_quantity: int): + if name in self.inventory: + item = self.inventory[name] + if item.quantity >= sold_quantity: + item.quantity -= sold_quantity + print(f"{Fore.GREEN}{sold_quantity} {name} sold. There are left {item.quantity}.{Style.RESET_ALL}") + return True + else: + print(f"{Fore.RED}Error: Not enough quantity. Ramaining: {item.quantity}{Style.RESET_ALL}") + return False + else: + print(f"{Fore.RED}Error: Item {name} not exist.{Style.RESET_ALL}") + return False + + def show_inventory(self): + print("\n--- Current Inventory ---") + if not self.inventory: + print("Inventory is empty.") + for item in self.inventory.values(): + print(item) + print("--------------------------") + + def save_inventory(self): + try: + with open(self.FILE_NAME, 'w') as f: + inventory_to_save = {} + for name, item in self.inventory.items(): + item_details = item.to_dict() + item_details['item_type'] = item.__class__.__name__ + inventory_to_save[name] = item_details + json.dump(inventory_to_save, f, indent=4) + print(f"{Fore.GREEN}Inventory saved.{Style.RESET_ALL}") + except IOError: + print(f"{Fore.RED}Error saving the file.{Style.RESET_ALL}") + + def load_inventory(self): + try: + with open(self.FILE_NAME, 'r') as f: + inventory_loaded = json.load(f) + self.inventory = {} + for name, item_details in inventory_loaded.items(): + item_type = item_details.pop('item_type') + if item_type == 'FoodItem': + self.inventory[name] = FoodItem.from_dict(item_details) + elif item_type == 'ElectronicItem': + self.inventory[name] = ElectronicItem.from_dict(item_details) + elif item_type == 'Item': + self.inventory[name] = Item.from_dict(item_details) + print(f"{Fore.GREEN}Inventory loaded.{Style.RESET_ALL}") + except (IOError, json.JSONDecodeError, TypeError) as e: + print(f"{Fore.YELLOW}No inventory existing or corrupted file. Error details: {e}{Style.RESET_ALL}") + self.inventory = {} \ No newline at end of file diff --git a/code/12/solutions/inventory-manager/main.py b/code/12/solutions/inventory-manager/main.py new file mode 100755 index 0000000..01b9538 --- /dev/null +++ b/code/12/solutions/inventory-manager/main.py @@ -0,0 +1,57 @@ +from colorama import Fore, Style +from inventory_manager import InventoryManager +from models import Item, FoodItem, ElectronicItem + +def menu(): + """Displays the menu and handles user input.""" + manager = InventoryManager() + while True: + manager.show_inventory() + print("\nOptions: (a)dd, (r)emove, (s)ave, (l)oad, (q)uit") + choice = input("Choose an option: ").lower() + + if choice == 'a': + item_type = input("What type of item do you want to add? (f)ood, (e)lectronic or (s)imple: ").lower() + name = input("Item name: ") + try: + price = float(input("Price: ")) + quantity = int(input("Quantity: ")) + if price <= 0 or quantity <= 0: + print(f"{Fore.RED}Error: Price and quantity must be greater than zero.{Style.RESET_ALL}") + continue + + if item_type == 'f': + expiration = input("Expiration date (dd-mm-yyyy): ") + new_item = FoodItem(name, price, quantity, expiration) + elif item_type == 'e': + warranty = int(input("Warranty (years): ")) + new_item = ElectronicItem(name, price, quantity, warranty) + elif item_type == 's': + new_item = Item(name, price, quantity) + else: + print(f"{Fore.RED}Invalid item type.{Style.RESET_ALL}") + continue + + manager.add_item(new_item) + except ValueError: + print(f"{Fore.RED}Error: Price, quantity and warranty must be valid numbers.{Style.RESET_ALL}") + + elif choice == 'r': + name = input("Item name to sell: ") + manager.sell_item(name) + + elif choice == 's': + manager.save_inventory() + + elif choice == 'l': + manager.load_inventory() + + elif choice == 'q': + print("Thank you, program terminated.") + break + + else: + print(f"{Fore.RED}Invalid option, try again.{Style.RESET_ALL}") + +if __name__ == "__main__": + menu() \ No newline at end of file diff --git a/code/12/solutions/inventory-manager/models.py b/code/12/solutions/inventory-manager/models.py new file mode 100755 index 0000000..efc07f4 --- /dev/null +++ b/code/12/solutions/inventory-manager/models.py @@ -0,0 +1,81 @@ +from abc import ABC, abstractmethod + +# Abstract base class for all products +class BaseItem(ABC): + def __init__(self, name: str, price: float, quantity: int): + self.name = name + self.__price = price + self.quantity = quantity + + # Abstract method that each subclass must implement + @abstractmethod + def __str__(self): + pass + + # Method for encapsulating the price + def get_price(self) -> float: + return self.__price + + # Method to set the price, with validation + def set_price(self, new_price: float): + if new_price > 0: + self.__price = new_price + else: + print("Price must be greater than 0.") + + def to_dict(self): + return {'name': self.name, 'price': self.get_price(), 'quantity': self.quantity} + + @classmethod + def from_dict(cls, data): + return cls(name=data['name'], price=data['price'], quantity=data['quantity']) + + +# Subclass for food products +class FoodItem(BaseItem): + def __init__(self, name: str, price: float, quantity: int, expiration_date: str): + super().__init__(name, price, quantity) + self.expiration_date = expiration_date + + def __str__(self): + return f"Food Item: {self.name.capitalize()} | Price: {self.get_price():.2f}€ | Quantity: {self.quantity} | Expiration Date: {self.expiration_date}" + + def to_dict(self): + data = super().to_dict() + data['expiration_date'] = self.expiration_date + return data + +# Subclass for electronic products +class ElectronicItem(BaseItem): + def __init__(self, name: str, price: float, quantity: int, warranty_years: int): + super().__init__(name, price, quantity) + self.warranty_years = warranty_years + + def __str__(self): + return f"Electronic Item: {self.name.capitalize()} | Price: {self.get_price():.2f}€ | Quantity: {self.quantity} | Years of warranty: {self.warranty_years}" + + def to_dict(self): + data = super().to_dict() + data['warranty_years'] = self.warranty_years + return data + +# Item class, as a reference for the previous version of the project +class Item: + """Class to represent a single product in the inventory.""" + def __init__(self, name: str, price: float, quantity: int): + self.name = name + self.price = price + self.quantity = quantity + + def __str__(self): + """Returns a string representation of the object.""" + return f"Item: {self.name.capitalize()} | Price: {self.price:.2f}€ | Quantity: {self.quantity}" + + def to_dict(self): + """Converts the object into a dictionary for saving.""" + return self.__dict__ + + @classmethod + def from_dict(cls, data): + """Creates an Item object from a dictionary.""" + return cls(**data) \ No newline at end of file diff --git a/code/12/solutions/inventory-manager/requirements.txt b/code/12/solutions/inventory-manager/requirements.txt new file mode 100755 index 0000000..e648e64 --- /dev/null +++ b/code/12/solutions/inventory-manager/requirements.txt @@ -0,0 +1 @@ +colorama==0.4.6 \ No newline at end of file diff --git a/code/12/solutions/inventory-manager/test_inventory.py b/code/12/solutions/inventory-manager/test_inventory.py new file mode 100755 index 0000000..a983b77 --- /dev/null +++ b/code/12/solutions/inventory-manager/test_inventory.py @@ -0,0 +1,67 @@ +import unittest +import os +from inventory_manager import InventoryManager +from models import Item, FoodItem, ElectronicItem + +class TestInventoryManager(unittest.TestCase): + + def setUp(self): + # Runs before each test. + # Ensures a clean test environment by removing the save file. + self.manager = InventoryManager() + if os.path.exists(self.manager.FILE_NAME): + os.remove(self.manager.FILE_NAME) + # Initializes a new manager that will start with an empty inventory + self.manager = InventoryManager() + self.test_item = Item("Test Item", 10.0, 5) + + def tearDown(self): + # Runs after each test. + # Cleans up by removing the save file created. + if os.path.exists(self.manager.FILE_NAME): + os.remove(self.manager.FILE_NAME) + + def test_add_item_success(self): + self.assertTrue(self.manager.add_item(self.test_item)) + self.assertEqual(len(self.manager.inventory), 1) + self.assertIn("Test Item", self.manager.inventory) + + def test_add_existing_item(self): + self.manager.add_item(self.test_item) + duplicate_item = Item("Test Item", 12.0, 3) + self.assertFalse(self.manager.add_item(duplicate_item)) + self.assertEqual(self.manager.inventory['Test Item'].quantity, 5) + + def test_sell_item_success(self): + self.manager.add_item(self.test_item) + self.assertTrue(self.manager.sell_item("Test Item", 3)) + self.assertEqual(self.manager.inventory['Test Item'].quantity, 2) + + def test_sell_item_insufficient_quantity(self): + self.manager.add_item(self.test_item) + self.assertFalse(self.manager.sell_item("Test Item", 10)) + self.assertEqual(self.manager.inventory['Test Item'].quantity, 5) + + def test_sell_nonexistent_item(self): + self.assertFalse(self.manager.sell_item("Nonexistent Item", 1)) + + def test_save_and_load_inventory(self): + food_item = FoodItem("Milk", 1.50, 10, "30-12-2025") + electronic_item = ElectronicItem("Laptop", 1200.0, 2, 2) + self.manager.add_item(food_item) + self.manager.add_item(electronic_item) + self.manager.save_inventory() + + # Create a new instance of InventoryManager to simulate a new program start + new_manager = InventoryManager() + + # Verify that the inventory was loaded correctly + self.assertIn("Milk", new_manager.inventory) + self.assertIn("Laptop", new_manager.inventory) + self.assertEqual(new_manager.inventory['Milk'].quantity, 10) + self.assertEqual(new_manager.inventory['Laptop'].get_price(), 1200.0) + self.assertIsInstance(new_manager.inventory['Milk'], FoodItem) + self.assertIsInstance(new_manager.inventory['Laptop'], ElectronicItem) + +if __name__ == '__main__': + unittest.main() diff --git a/docs/index.html b/docs/index.html old mode 100644 new mode 100755 diff --git a/docs/quiz.html b/docs/quiz.html old mode 100644 new mode 100755