Usprawnij programowanie dzięki Gemini Code Assist

1. Wprowadzenie

e5b98fd4e417c877.png

W ramach tego ćwiczenia w Codelabs dowiesz się, jak Gemini Code Assist może pomóc Ci na kluczowych etapach cyklu życia oprogramowania (SDLC), takich jak projektowanie, tworzenie testowania i wdrażania. Zaprojektujemy i opracujemy całą aplikację, a potem wdrożymy ją w Google Cloud.

Będziemy tworzyć interfejs API i aplikację, które będą umożliwiały przeszukiwanie sesji w przypadku zdarzeń technicznych. Każda sesja będzie mieć tytuł, podsumowanie, czas trwania, kategorie oraz co najmniej jednego prelegenta.

Co trzeba zrobić

  • Projektowanie, kompilowanie, testowanie i wdrażanie aplikacji internetowej od zera w oparciu o specyfikację OpenAPI

Czego się nauczysz

  • Jak używać Gemini Code Assist do generowania specyfikacji OpenAPI
  • Jak używać funkcji generowania kodu w Gemini Code Assist do opracowywania aplikacji Python Flask na potrzeby specyfikacji OpenAPI
  • Jak używać Gemini Code Assist do wygenerowania interfejsu internetowego dla aplikacji w Pythonie Flask
  • Jak korzystać z Gemini Code Assist, aby uzyskać pomoc we wdrażaniu aplikacji w Google Cloud Run
  • Korzystaj z funkcji Gemini Code Assist, takich jak objaśnianie kodu i generowanie przypadków testowych, oraz podczas tworzenia i testowania aplikacji

Czego potrzebujesz

  • Przeglądarka Chrome
  • konto Gmail,
  • Projekt Cloud z włączonymi płatnościami
  • Usługa Gemini Code Assist została włączona w Twoim projekcie Cloud

Ten moduł jest przeznaczony dla programistów na wszystkich poziomach zaawansowania, w tym początkujących. Chociaż przykładowa aplikacja jest w języku Python, nie musisz znać się na programowaniu, by zrozumieć, co się dzieje. Skupimy się na poznaniu możliwości Gemini Code Assist.

2. Skonfiguruj Gemini Code Assist

W tej sekcji znajdziesz informacje o wszystkim, co jest potrzebne, aby rozpocząć korzystanie z tego modułu.

Włącz Gemini Code Assist w Cloud Shell IDE

Do pozostałej części tych ćwiczeń będziemy używać Cloud Shell IDE – w pełni zarządzanego środowiska programistycznego opartego na Code OSS. Musimy włączyć i skonfigurować funkcję Code Assist w IDE Cloud Shell. Poniżej znajdziesz instrukcje:

  1. Otwórz stronę ide.cloud.google.com. IDE może pojawić się dopiero po jakimś czasie, dlatego zachowaj cierpliwość i zaakceptuj wszystkie domyślne opcje konfiguracji. Jeśli potrzebujesz instrukcji konfigurowania IDE, dokończ je z ustawieniami domyślnymi.
  2. Kliknij przycisk Cloud Code – Zaloguj się na dolnym pasku stanu, jak pokazano na ilustracji. Autoryzuj wtyczkę zgodnie z instrukcjami. Jeśli na pasku stanu widzisz komunikat „Cloud Code – no project” (Cloud Code – brak projektu), zaznacz tę pozycję, a następnie wybierz konkretny projekt Google Cloud z listy projektów, z którymi zamierzasz pracować.

6f5ce865fc7a3ef5.png

  1. W prawym dolnym rogu kliknij przycisk Code Assist (Wspomaganie kodu), jak pokazano na ilustracji, i po raz ostatni wybierz odpowiedni projekt Google Cloud. Jeśli pojawi się prośba o włączenie interfejsu Cloud AI Companion API, zrób to i przejdź dalej.
  2. Po wybraniu projektu Google Cloud sprawdź, czy widzisz komunikat o stanie Cloud Code na pasku stanu oraz czy masz włączoną funkcję Code Assist po prawej stronie, na pasku stanu, jak pokazano poniżej:

709e6c8248ac7d88.png

Możesz już korzystać z Gemini Code Assist.

3. Skonfiguruj Firestore

Cloud Firestore to w pełni zarządzana bezserwerowa baza danych dokumentów, która będzie używana jako backend dla danych naszych aplikacji. Dane w Cloud Firestore są uporządkowane w kolekcjach dokumentów.

Musimy utworzyć kolekcję o nazwie sessions w naszej domyślnej bazie danych Firestore. Ta kolekcja będzie zawierać przykładowe dane (dokumenty), które wykorzystamy w naszej aplikacji.

Otwórz terminal w środowisku IDE Cloud Shell za pomocą menu głównego, jak pokazano poniżej:

f1535e14c9beeec6.png

Musimy utworzyć kolekcję o nazwie sessions. Znajdziesz w niej listę przykładowych dokumentów sesji. Każdy dokument ma następujące atrybuty:

  1. title: ciąg znaków
  2. kategorie: tablica ciągów znaków,
  3. speakers: tablica ciągów znaków.
  4. duration: ciąg znaków
  5. summary: ciąg znaków

Wypełnijmy tę kolekcję przykładowymi danymi, kopiując plik zawierający te dane do zasobnika w Twoim projekcie, skąd możesz potem zaimportować tę kolekcję za pomocą polecenia gcloud firestore import.

Inicjowanie bazy danych Firestore

Otwórz stronę Firestore w konsoli Cloud.

Jeśli baza danych Firestore nie została wcześniej zainicjowana w projekcie, utwórz bazę danych default. Podczas tworzenia bazy danych użyj tych wartości:

  • Tryb Firestore: Native
  • Lokalizacja: jako typ lokalizacji wybierz Region, a następnie wybierz region odpowiedni dla Twojej aplikacji. Zanotuj tę lokalizację, ponieważ będzie Ci ona potrzebna w następnym kroku w przypadku lokalizacji zasobnika.
  • Utworzenie bazy danych.

504cabdb99a222a5.png

Utworzymy teraz kolekcję sessions w następujący sposób:

  1. Utwórz zasobnik w projekcie za pomocą podanego poniżej polecenia gsutil. Zastąp zmienną <PROJECT_ID> w poniższym poleceniu identyfikatorem projektu Google Cloud. Zastąp <BUCKET_LOCATION> nazwą regionu odpowiadającą obszarowi geograficznemu domyślnej bazy danych Firestore (jak wskazano w poprzednim kroku). Może to być US-WEST1, EUROPE-WEST1, ASIA-EAST1 :
gsutil mb -l <BUCKET-LOCATION> gs://<PROJECT_ID>-my-bucket
  1. Zasobnik został już utworzony, więc musimy skopiować przygotowany przez nas eksport bazy danych do tego zasobnika, zanim będziemy mogli go zaimportować do bazy danych Firebase. Użyj tego polecenia:
gsutil cp -r gs://sessions-master-database-bucket/2024-03-26T09:28:15_95256  gs://<PROJECT_ID>-my-bucket

Mamy już dane do zaimportowania, możemy więc przejść do ostatniego kroku, czyli importowania ich do utworzonej przez nas bazy danych Firebase (default).

  1. Użyj podanego niżej polecenia gcloud:
gcloud firestore import gs://<PROJECT_ID>-my-bucket/2024-03-26T09:28:15_95256

Import potrwa kilka sekund. Po zakończeniu możesz zweryfikować bazę danych Firestore oraz kolekcję, otwierając stronę https://console.cloud.google.com/firestore/databases, wybierając bazę danych default i kolekcję sessions, jak pokazano poniżej:

d3e294d46ba29cd5.png

Spowoduje to zakończenie tworzenia kolekcji Firestore, która będzie używana w naszej aplikacji.

4. Tworzenie szablonu aplikacji

Przygotujemy przykładową aplikację (aplikację w języku Python Flask), której będziemy używać w pozostałych ćwiczeniach z programowania. Ta aplikacja umożliwia wyszukiwanie w sesjach odbywających się na konferencji technicznej.

Aby to zrobić:

  1. Kliknij nazwę projektu Google Cloud na pasku stanu poniżej.

f151759c156c124e.png

  1. Pojawi się lista opcji. Na liście poniżej kliknij New Application (Nowa aplikacja).

91ea9836f38b7f74.png

  1. Wybierz Cloud Run application (Aplikacja Cloud Run) (będzie to środowisko wykonawcze naszej aplikacji).
  2. Wybierz szablon aplikacji Python (Flask): Cloud Run.
  3. Nadaj aplikacji nazwę i zapisz ją w wybranej lokalizacji.
  4. Zostanie wyświetlone powiadomienie z potwierdzeniem utworzenia aplikacji oraz otwarcie nowego okna z aplikacją załadowaną w sposób pokazany poniżej. Otwarto plik README.md. Możesz na razie zamknąć ten widok.

aaa3725b17ce27cf.png

5. Interakcja z Gemini Code Assist

Na potrzeby tego modułu będziemy używać Gemini Code Assist Chat dostępnego w Cloud Shell IDE w ramach rozszerzenia Cloud Code w VS Code. Można je wyświetlić, klikając przycisk Asystent kodu na pasku nawigacyjnym po lewej stronie. Znajdź ikonę Code Assist a489f98a34898727.png na lewym pasku nawigacyjnym i kliknij ją.

Spowoduje to wyświetlenie panelu czatu pomocy Code Assist w IDE Cloud Shell, gdzie możesz rozpocząć czat z pomocą Code Assist.

14ad103efaa0ddaa.png

Zwróć uwagę na ikonę kosza u góry – to dobry sposób na zresetowanie kontekstu historii czatu z Asystentem do tworzenia kodu. Pamiętaj też, że interakcja na czacie jest uzależniona od kontekstu w odniesieniu do plików, nad którymi pracujesz w IDE.

6. Projektowanie interfejsów API

Na etapie projektowania będziemy korzystać z pomocy Gemini Code Assist. W tym kroku wygenerujemy specyfikację OpenAPI dla jednostek (sesji technicznych w przypadku zdarzenia), które chcemy przeszukać.

Napisz prompt:

Generate an Open API spec that will allow me to retrieve all sessions, sessions by category, session by id. Each session has the following attributes: id, title, list of speakers, list of categories, summary and duration.

Powinno to spowodować wygenerowanie specyfikacji OpenAPI do przeszukiwania sesji przy użyciu różnych parametrów zapytania. Poniżej znajduje się przykładowa specyfikacja:

openapi: 3.0.0
info:
 title: Sessions API
 description: This API allows you to retrieve all sessions, sessions by category, and session by id.
 version: 1.0.0
servers:
 - url: https://sessions.example.com
paths:
 /sessions:
   get:
     summary: Get all sessions
     operationId: getSessions
     responses:
       '200':
         description: OK
         content:
           application/json:
             schema:
               type: array
               items:
                 $ref: '#/components/schemas/Session'
 /sessions/{id}:
   get:
     summary: Get session by id
     operationId: getSessionById
     parameters:
       - name: id
         in: path
         required: true
         description: The id of the session
         schema:
           type: string
     responses:
       '200':
         description: OK
         content:
           application/json:
             schema:
               $ref: '#/components/schemas/Session'
 /sessions/categories/{category}:
   get:
     summary: Get sessions by category
     operationId: getSessionsByCategory
     parameters:
       - name: category
         in: path
         required: true
         description: The category of the sessions
         schema:
           type: string
     responses:
       '200':
         description: OK
         content:
           application/json:
             schema:
               type: array
               items:
                 $ref: '#/components/schemas/Session'
components:
 schemas:
   Session:
     type: object
     properties:
       id:
         type: string
         description: The id of the session
       title:
         type: string
         description: The title of the session
       speakers:
         type: array
         items:
           type: string
         description: The list of speakers for the session
       categories:
         type: array
         items:
           type: string
         description: The list of categories for the session
       summary:
         type: string
         description: The summary of the session
       duration:
         type: string
         description: The duration of the session

Specyfikacja zawiera:

  • Schemat zdefiniowany dla typu sesji.
  • Zdefiniowano kilka ścieżek interfejsu API:
  • /sessions
  • /sessions/{id}
  • /sessions/categories/{category}

Utwórz w górnym folderze plik o nazwie sessionsapi.yaml i skopiuj treść z okna czatu z Asystentem ds. kodu, używając opcji „wstaw bieżący plik”. (przycisk +) i pozostaw plik otwarty w środowisku IDE Cloud Shell.

Na tym etapie warto zwrócić uwagę na interesującą funkcję Gemini Code Assist: cytowanie. Te informacje są ujawniane deweloperowi, gdy wygenerowany kod bezpośrednio zawiera cytaty z innego źródła, np. istniejącego kodu open source. Deweloper zawiera źródło i licencję, dzięki czemu deweloper decyduje, co z nim zrobić.

Zakładając, że wszystko jest w porządku z wygenerowanymi treściami, możemy teraz użyć tego dokumentu specyfikacji do wygenerowania aplikacji w języku Python Flask.

7. Generowanie aplikacji

Poprosimy teraz usługę Code Assist o wygenerowanie aplikacji. Przekaż ten prompt przy otwartym pliku sessionsapi.yaml.

Generate a Python Application using the Flask framework, based on the sessionsapi.yaml file. This application uses a local in memory list of sessions. Do not use any Flask extensions.

W ten sposób otrzymasz szkielet aplikacji w języku Python Flask oparty na funkcjach i ścieżkach określonych w pliku specyfikacji OpenAPI.

Podany kod aplikacji Python Flask powinien być podobny do tego:

from flask import Flask, jsonify, request

app = Flask(__name__)

sessions = [
    {
        "id": "1",
        "title": "Session 1",
        "speakers": ["Speaker 1", "Speaker 2"],
        "categories": ["Category 1", "Category 2"],
        "summary": "This is a summary of session 1.",
        "duration": "1 hour",
    },
    {
        "id": "2",
        "title": "Session 2",
        "speakers": ["Speaker 3", "Speaker 4"],
        "categories": ["Category 3", "Category 4"],
        "summary": "This is a summary of session 2.",
        "duration": "1 hour 30 minutes",
    },
]

@app.route('/sessions', methods=['GET'])
def get_sessions():
    return jsonify(sessions)

@app.route('/sessions/<id>', methods=['GET'])
def get_session_by_id(id):
    session = next((session for session in sessions if session['id'] == id), None)
    if session is None:
        return jsonify({}), 404
    return jsonify(session)

@app.route('/sessions/categories/<category>', methods=['GET'])
def get_sessions_by_category(category):
    sessions_by_category = [session for session in sessions if category in session['categories']]
    return jsonify(sessions_by_category)

if __name__ == '__main__':
    app.run()

W poprzednim kroku został wygenerowany istniejący plik app.py. Wystarczy zastąpić zawartość pliku kodem wygenerowanym przez narzędzie Code Assist, a następnie zapisać plik.

Chcielibyśmy zmienić wiersz app.run() na port 8080 i adres hosta 0.0.0.0 oraz uruchomić go w trybie debugowania podczas wykonywania lokalnego.Oto jak to zrobić. Na początek zaznaczmy/zaznaczmy linię:

app.run()

Następnie w interfejsie czatu z Asystentem kodu wpisz prompt: Explain this.

Powinno wyświetlić się szczegółowe objaśnienie tego konkretnego wiersza, którego przykład przedstawiono poniżej:

58ec896a32a4fb68.png

Teraz użyj tego promptu:

update the code to run the application on port 8080, host address 0.0.0.0, and in debug mode

Wygenerowany sugerowany kod powinien wyglądać tak: :

app.run(host='0.0.0.0', port=8080, debug=True)

Pamiętaj, aby umieścić w pliku app.py ten fragment kodu.

Uruchamianie aplikacji lokalnie

Uruchommy teraz aplikację lokalnie, aby sprawdzić, czy są spełnione wymagania na początku.

Pierwszym krokiem będzie utworzenie wirtualnego środowiska Pythona z zależnościami pakietów Pythona z pliku requirements.txt, które zostanie zainstalowane w środowisku wirtualnym. Aby to zrobić, otwórz Paleta poleceń (Ctrl+Shift+P) w Cloud Shell IDE i wpisz Utwórz środowisko Pythona. Wykonaj kilka kolejnych czynności, aby wybrać środowisko wirtualne (venv), interpreter języka Python 3.x i plik requirements.txt.

Po utworzeniu środowiska otwórz nowe okno terminala (Ctrl+Shift+`) i podaj to polecenie:

python app.py

Oto przykładowe wykonanie:

(.venv) romin@cloudshell: $ python app.py 
 * Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:8080
 * Running on http://10.88.0.3:8080
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 132-247-368

Podgląd interfejsu API możesz teraz wyświetlić pod tymi adresami URL. Zakładamy, że Twój serwer programistyczny działa na porcie 8080. Jeśli nie, zmień go na odpowiedni.

  • https://<host-name>:8080/sessions
  • https://<host-name>:8080/sessions/{id}
  • https://<host-name>:8080/sessions/categories/{category}

Aby mieć pewność, że możesz pobrać dane JSON zawarte w pliku app.py, wykonaj te czynności:

Otwórz nowe okno terminala i wypróbuj dowolne z tych poleceń:

curl -X GET http://127.0.0.1:8080/sessions
curl -X GET http://127.0.0.1:8080/sessions/<ID>
curl -X GET http://127.0.0.1:8080/sessions/categories/<CATEGORY_NAME> 

8. Refaktoryzacja kodu

Zamiast elementu app.py zakodowanego na stałe przykładowego kodu JSON można chcieć je rozdzielić/wyodrębnić do innego modułu, tak aby można było zachować czysty rozgraniczenie kodu od danych. Zróbmy to!

Nie zamykaj pliku app.py i wyświetl ten prompt:

Can I improve this code and separate out the sessions data from this app.py file?

Zobaczysz kilka sugestii, które pomogą Ci to zrobić. Poniżej znajduje się przykładowa sugestia, którą otrzymaliśmy, która powinna być do Ciebie podobna:

9b9c56cb527dac4c.png

Rozdzielmy dane do pliku sessions.py zgodnie z zaleceniami zespołu Code Assist.

Utwórz nowy plik o nazwie sessions.py

, zawartość listy JSON (zgodnie z wygenerowanymi przez nas danymi) jest podana poniżej:

sessions = [
   {
       "id": "1",
       "title": "Session 1",
       "speakers": ["Speaker 1", "Speaker 2"],
       "categories": ["Category 1", "Category 2"],
       "summary": "This is a summary of session 1.",
       "duration": "1 hour",
   },
   {
       "id": "2",
       "title": "Session 2",
       "speakers": ["Speaker 3", "Speaker 4"],
       "categories": ["Category 3", "Category 4"],
       "summary": "This is a summary of session 2.",
       "duration": "1 hour 30 minutes",
   },
]

Plik app.py został znacznie uproszczony i przedstawia go poniżej:

from flask import Flask, jsonify, request
from sessions import sessions

app = Flask(__name__)

@app.route('/sessions', methods=['GET'])
def get_sessions():
   return jsonify(sessions.sessions)

@app.route('/sessions/<id>', methods=['GET'])
def get_session_by_id(id):
   session = next((session for session in sessions.sessions if session['id'] == id), None)
   if session is None:
       return jsonify({}), 404
   return jsonify(session)

@app.route('/sessions/categories/<category>', methods=['GET'])
def get_sessions_by_category(category):
   sessions_by_category = [session for session in sessions.sessions if category in session['categories']]
   return jsonify(sessions_by_category)

if __name__ == '__main__':
   app.run(host='0.0.0.0', port=8080, debug=True)

Sprawdź, czy nadal możesz uruchamiać aplikację lokalnie i korzystać z interfejsu API po wprowadzeniu tych zmodyfikowanych zmian. Serwer programistyczny w języku Python prawdopodobnie nadal działa, więc wystarczy odwołać się do poleceń curl z poprzedniego kroku.

9. Integracja z kolekcją Firestore

Następnym krokiem jest odejście od lokalnej listy JSON w pamięci dla naszych sesji i połączenie aplikacji z kolekcją sesji w bazie danych Firestore, którą utworzyliśmy na początku tego ćwiczenia.

Nie zamykaj pliku sessions.py i wyświetl ten prompt:

Can you further refactor the sessions.py code to read from a Firestore database that has a collection named sessions. The collection has the same attributes as the session object that we have defined. Use the Python module google-cloud-firestore. 

Otrzymaliśmy tę sugestię dotyczącą odczytywania wszystkich sesji z kolekcji Firestore:

import google.cloud.firestore

# Create a Firestore client
db = google.cloud.firestore.Client()

# Get the sessions collection
sessions_collection = db.collection("sessions")

# Create a list of sessions
sessions = []

# Iterate over the documents and add them to the list
for doc in sessions_collection.stream():
   session = doc.to_dict()
   session["id"] = doc.id
   sessions.append(session)

Możesz go umieścić w witrynie sessions.py.

Jeśli serwer Flask Development Server działa lokalnie, możliwe, że Twoja aplikacja została zamknięta z powodu braku modułu Pythona.

Możesz na przykład zapytać Code Assist na przykład, który moduł Pythona należy dodać do pliku requirements.txt, na przykład:

Which Python package needs to be installed to make the firestore code work?

Zobaczysz nazwę modułu Pythona (np. google-cloud-firestore). Dodaj go do pliku requirements.txt.

Musisz ponownie utworzyć środowisko Pythona z nowo dodanym modułem (google-cloud-firestore). Aby to zrobić, wpisz to polecenie w dotychczasowym oknie terminala:

pip install -r requirements.txt

Uruchom aplikację jeszcze raz (za pomocą polecenia python app.py) i wejdź na adres URL /sessions. Powinny wyświetlić się przykładowe dokumenty, które dodaliśmy do kolekcji sessions.

975d05e6518f1a6a.png

Możesz wysyłać zapytania o inne identyfikatory URI, aby pobrać określone sesje lub wszystkie sesje z danej kategorii, jak opisano w poprzednich krokach.

10. Objaśnienie kodu

Teraz jest dobry moment, aby skorzystać z funkcji "Explain this" dostępnej w Gemini Code Assist, aby lepiej zrozumieć kod. Otwórz dowolny z tych plików lub wybierz konkretny fragment kodu i zapytaj zespół pomocy ds. kodu, podając następujący prompt: Explain this.

W ramach ćwiczenia otwórz plik sessions.py i zaznacz kod Firestore oraz sprawdź jego wyjaśnienie. Spróbuj użyć tej funkcji również w innych plikach w projekcie, nie tylko w kodzie Pythona.

11. Wygeneruj aplikację internetową

Interfejs API został wygenerowany i zintegrowany z aktywną kolekcją Firestore, więc możemy wygenerować internetowy interfejs aplikacji. Funkcjonalność naszego interfejsu internetowego będzie obecnie ograniczona do minimum, co oznacza możliwość wyszukiwania sesji należących do konkretnej kategorii. Pamiętaj, że mamy ścieżkę API do tego adresu, np. /sessions/categories/{category}, więc nasza aplikacja internetowa powinna ją wywołać i pobrać wyniki.

Zaczynajmy. Wyślij ten prompt do Code Assist:

Generate a web application that allows me to search for sessions by category and uses the Flask application that we created. Please use basic HTML, CSS and JS. Embed all the Javascript and CSS code into a single HTML file only.

Spowoduje to wygenerowanie kodu HTML aplikacji internetowej z umieszczonymi w niej skryptami JavaScript i arkuszami CSS. Pojawi się też prośba o dodanie nowej trasy do pliku app.py, dzięki czemu każdy użytkownik odwiedzający główny lub podstawowy adres URL będzie widzieć stronę główną. Jeśli informacje nie zawierają tej informacji, zapytaj o nie lub użyj podanego niżej fragmentu:

@app.route('/')
def index():
   return render_template('index.html')

Możesz zapisać ten plik jako index.html, ale możesz mieć wątpliwości, gdzie zapisać ten plik (czyli w jakim folderze?). Możemy zadać kolejne pytanie zespołowi Code Assist.

Given that I am using the flask framework, where should I put the index.html file?

Powinien zawierać jasne informacje o używaniu platformy render_template, dlatego plik index.html trzeba umieścić w folderze templates. Ten folder jest dostępny, ponieważ na początku tego ćwiczenia z programowania utworzyliśmy aplikację na podstawie szablonu Flask. W związku z tym plik index.html istnieje, więc musisz po prostu zastąpić jego zawartość nowym plikiem, który został tu wygenerowany. Asystent Code pomaga też zaimportować adres render_template do pliku app.py.

Zapisz kod aplikacji internetowej w pliku index.html i pamiętaj, by umieścić ten plik w folderze templates.

Uruchamianie aplikacji lokalnie

Uruchommy teraz aplikację lokalnie, aby sprawdzić, czy są spełnione wymagania na początku.

Sprawdź, czy lokalny serwer Flask nadal działa i nie wyświetla żadnych błędów. W przypadku błędów napraw je i uruchom serwer.

Po uruchomieniu aplikacji przejdź do jej głównego adresu URL. Powinien wyświetlić się plik index.html podobny do tego:

8ca586acc4536879.png

Podaj jedną z kategorii danych jako dane wejściowe (np. AI) i kliknij przycisk Search. Powinno to spowodować wyświetlenie sesji otagowanych za pomocą kategorii AI.

165faded790a6c.png

Opcjonalnie możesz wyświetlić dodatkowe metadane, takie jak czas trwania, kategorie, prelegenci i podsumowanie każdej sesji.

12. Generowanie przypadku testowego

Wygenerowaliśmy interfejs Session API. Teraz możesz skorzystać z Gemini Code Assist do generowania przypadków testowania jednostkowego dla różnych ścieżek API.

Nie zamykaj pliku app.py i wyświetl ten prompt:

Generate unit test cases for app routes. Use unittest module.

Otrzymaliśmy tę odpowiedź:

import unittest
from app import app

class FlaskAppTestCase(unittest.TestCase):

    def test_index(self):
        tester = app.test_client()
        response = tester.get('/')
        self.assertEqual(response.status_code, 200)

    def test_get_sessions(self):
        tester = app.test_client()
        response = tester.get('/sessions')
        self.assertEqual(response.status_code, 200)

    def test_get_session_by_id(self):
        tester = app.test_client()
        response = tester.get('/sessions/1')
        self.assertEqual(response.status_code, 200)

    def test_get_sessions_by_category(self):
        tester = app.test_client()
        response = tester.get('/sessions/categories/category1')
        self.assertEqual(response.status_code, 200)

if __name__ == '__main__':
    unittest.main()

Utwórz plik o nazwie tests.py z użyciem tego kodu.

Uwaga na temat generowania przypadków testowych

Wpis może być inny niż wymieniony powyżej, co może powodować problemy z testowaniem. Na przykład: zaobserwowaliśmy, że w niektórych naszych uruchomieniach pominięto następujące ważne fragmenty kodu:

from app import app

Powyższy kod jest wymagany do zaimportowania dotychczasowej aplikacji Flask, w której będziemy wywoływać przypadki testowe.

if __name__ == '__main__':

`unittest.main()`

Powyższy kod jest potrzebny do uruchamiania przypadków testowych.

Zalecamy sprawdzenie każdego przypadku testowego, sprawdzenie assertEqual i innych warunków w wygenerowanym kodzie, aby upewnić się, że wszystko działa. Ponieważ dane są zewnętrzne w kolekcji Firestore, może ona nie mieć do nich dostępu i może używać pewnych danych, w wyniku czego testy mogą zakończyć się niepowodzeniem. Odpowiednio zmodyfikuj przypadki testowe lub opisz przypadki, których nie potrzebujesz od razu.

W ramach demonstracji uruchomiliśmy przypadki testowe za pomocą tego polecenia (pamiętaj, aby uruchomić lokalny serwer programistyczny, ponieważ wywołania zostaną wysłane do lokalnych punktów końcowych interfejsu API):

python tests.py

Oto wynik podsumowania:

Ran 4 tests in 0.274s

FAILED (failures=2)

To rzeczywiście dobra odpowiedź, ponieważ w 3. teście identyfikator sesji był nieprawidłowy i nie ma kategorii o nazwie category1.

Należy więc odpowiednio dostosować przypadki testowe i przetestować je.

13. Programowanie oparte na testach

Teraz przyjrzymy się dodawaniu do interfejsu API sesji nowej metody wyszukiwania zgodnie z metodologią TDD (Test Driven Development). Polega ona na pisaniu przypadków testowych, przez co nie spełniają swoich oczekiwań z powodu braku implementacji, i wykorzystamy narzędzie Gemini Code Assist do wygenerowania brakującej implementacji, która pozwoli na zaliczenie testu.

Otwórz plik Test.py (przy założeniu, że w pliku tests.py zostały poprawione wszystkie testy, które zostały zaliczone). Poproś zespół Code Assist o następujący prompt:

Generate a new test case to search for sessions by speaker

W efekcie uzyskaliśmy taką implementację przypadku testowego, którą prawidłowo wstawiliśmy do pliku tests.py.

  def test_get_sessions_by_speaker(self):
        tester = app.test_client()
        response = tester.get('/sessions/speakers/speaker1')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json, [sessions.sessions[0], sessions.sessions[1]])

Po uruchomieniu testów powinien wyświetlić się ten błąd:

$ python tests.py 
.F.
======================================================================
FAIL: test_get_sessions_by_speaker (__main__.FlaskAppTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/romin/hello-world-5/tests.py", line 21, in test_get_sessions_by_speaker
    self.assertEqual(response.status_code, 200)
AssertionError: 404 != 200

----------------------------------------------------------------------
Ran 3 tests in 0.010s

FAILED (failures=1)

Dzieje się tak, ponieważ przypadek testowy wywołał tę ścieżkę (/sessions/speakers/), której nie można zaimplementować w app.py.

Poprośmy Code Assist o implementację. Otwórz plik app.py i podaj ten prompt do Code Assist:

Add a new route to search for sessions by a specific speaker

W pliku app.py dodaliśmy taką implementację sugerowaną przez zespół Code Assist:

@app.route('/sessions/speakers/<speaker>', methods=['GET'])
def get_sessions_by_speaker(speaker):
    sessions_by_speaker = [session for session in sessions.sessions if speaker in session['speakers']]
    return jsonify(sessions_by_speaker)

Otwórz plik tests.py i zmodyfikowaliśmy przypadek testowy w ten sposób:

   def test_get_sessions_by_speaker(self):
       tester = app.test_client()
       response = tester.get('/sessions/speakers/Romin Irani')
       self.assertEqual(response.status_code, 200)
       self.assertEqual(len(response.json), 1)

Test przebiegł bez problemów. Pozostawiamy to zadanie w celu przyjrzenia się wygenerowanym przypadkom testowym i pewnego stopnia ich modyfikacji w zależności od danych, które możesz mieć w Firestore, oraz użycia odpowiednich metod assert* w przykładach do testów jednostkowych w Pythonie.

14. Wdrażanie w Google Cloud Run

Skoro wiemy już, jaka jest jakość naszych procesów programistycznych, ostatnim krokiem będzie wdrożenie tej aplikacji w Google Cloud Run. Ale być może, w trosce o bezpieczeństwo, powinniśmy zapytać pomocy Code Assist, jeśli o czymś zapomnieliśmy. Po otwarciu strony app.py prześlij ten prompt :

Is there something here I should change before I deploy to production?

Twoja prośba to dobra odpowiedź, bo zapomnieliśmy ustawić flagę debugowania na wyłączone :

2f87ed3a811fb218.png

Jak wskazano, wyłącz debugowanie i poproś zespół pomocy Gemini Code Assist o pomoc w sprawie polecenia gcloud, którego można użyć do wdrożenia aplikacji w Cloud Run bezpośrednio ze źródła (bez konieczności wcześniejszego utworzenia kontenera).

Napisz prompt:

I would like to deploy the application to Cloud Run directly from source. What is the gcloud command to do that?

Wypróbuj kilka odmian powyższego promptu. Inny, który próbowaliśmy zastosować, to:

I would like to deploy this application to Cloud Run. I don't want to build a container image locally but deploy directly from source to Cloud Run. What is the gcloud command for that?

Najlepiej jest uzyskać to polecenie gcloud:

gcloud run deploy sessions --source .

Możesz też otrzymać:

gcloud run deploy <service-name> --source . \
—-platform managed \
—-allow-unauthenticated

Wykonaj podane wyżej polecenie w folderze głównym aplikacji. Gdy pojawi się pytanie o region, wybierz us-central1, a gdy pojawi się prośba o zezwolenie na korzystanie z unauthenticated invocations, wybierz Y. Może też pojawić się prośba o włączenie interfejsów Google Cloud API, takich jak Artifact Registry, Cloud Build i Cloud Run, oraz uprawnienia do utworzenia repozytorium Artifact Registry. Przyznaj mu te uprawnienia.

Proces wdrożenia potrwa około 2 minut, dlatego prosimy o cierpliwość.

Po wdrożeniu zostanie wyświetlony adres URL usługi Cloud Run. Otwórz ten publiczny adres URL. Ta sama aplikacja powinna zostać wdrożona i uruchomiona.

c5322d0fd3e0f616.png

Gratulacje, dobra robota!

15. (Opcjonalnie) Użyj Cloud Logging

Możemy wprowadzić logowanie w naszej aplikacji, aby logi aplikacji były scentralizowane w jednej z usług Google Cloud (Cloud Logging). Możemy wtedy też użyć funkcji dostrzegalności Gemini, aby zrozumieć wpisy logu.

Aby to zrobić, musimy najpierw użyć istniejącej biblioteki Python Cloud Logging z Google Cloud i używać jej do rejestrowania informacji informacyjnych, ostrzeżeń i komunikatów o błędach (w zależności od logu lub poziomu ważności).

Spróbujmy najpierw zapytać o to zespół pomocy Code Assist. Wypróbuj ten prompt:

How do I use the google-cloud-logging package in Python?

Powinna Ci się wyświetlić odpowiedź z takimi informacjami na ten temat:

2472e1ccaf8a217d.png

Dodajmy instrukcje rejestrowania do funkcji, która wyszukuje sesje według kategorii.

Najpierw dodaj pakiet google-cloud-logging Pythona do pliku requirements.txt.

Następny jest fragment kodu, który pokazuje, jak zintegrowaliśmy kod implementujący logowanie:

...
from google.cloud import logging
...
app = Flask(__name__)

# Create a logger
logger = logging.Client().logger('my-log')

@app.route('/sessions/categories/<category>', methods=['GET'])
def get_sessions_by_category(category):
   logger.log_text(f"Fetching sessions with category {category}")
   sessions_by_category = [session for session in sessions.sessions if category in session['categories']]
   logger.log_text(f'Found {len(sessions_by_category)} sessions with category {category}')
   return jsonify(sessions_by_category)

# # Other App Routes

Wdróż usługę ponownie w Cloud Run przy użyciu tego samego polecenia co w poprzedniej sekcji, a po wdrożeniu wykonaj kilka wywołań do punktu końcowego /sessions/categories/<category>.

Otwórz stronę Cloud Console → Logs Explorer

59e297577570695.png

...powinny być możliwe filtrowanie do tych instrukcji logowania, jak pokazano poniżej:

914f1fb6cac30a89.png

Możesz kliknąć dowolną pozycję dziennika, rozwinąć ją i kliknąć Explain this log entry, aby użyć Gemini do wyjaśnienia danego wpisu. Pamiętaj, że jeśli nie masz włączonej usługi Gemini w Google Cloud, pojawi się prośba o włączenie interfejsu Cloud AI Companion API. Wykonaj je zgodnie z instrukcjami.

Przykładowa odpowiedź:

7fc9783910fa92cc.png

16. Gratulacje

Gratulujemy! Udało Ci się utworzyć aplikację od zera i korzystać z Gemini Code Assist w wielu aspektach SDLC, w tym projektowaniu, kompilowaniu, testowaniu i wdrażaniu.

Co dalej?

Zapoznaj się z tymi ćwiczeniami z programowania...

Dokumentacja