1. Khoanh vùng trọng tâm
Ưu tiên các mạch kiến thức có khả năng ra tự luận: FR1-FR2-FR3, kiến trúc backend/frontend/database, Docker, Git/GitHub, PostgreSQL/.env, SQLAlchemy/Alembic, seed.py, web services và FastAPI/Uvicorn.
Ôn tự luận cho 20/05/2026
Các đề bám 11 bài trong thư mục moi và ngân hàng trắc nghiệm: yêu cầu hệ thống, phân tích kiến trúc, Git, PostgreSQL, ORM/Alembic, seeding, web services, FastAPI và tư duy học thời AI.
Ưu tiên các mạch kiến thức có khả năng ra tự luận: FR1-FR2-FR3, kiến trúc backend/frontend/database, Docker, Git/GitHub, PostgreSQL/.env, SQLAlchemy/Alembic, seed.py, web services và FastAPI/Uvicorn.
Không học tên công cụ rời rạc. Mỗi công cụ phải trả lời được: dùng ở đâu, giải quyết yêu cầu nào, dữ liệu đi qua nó ra sao, lỗi sai thường gặp là gì.
Biết: nêu khái niệm, lệnh, vai trò. Hiểu: giải thích vì sao dùng. Áp dụng: xử lý tình huống, thiết kế luồng, sửa lỗi cấu hình.
Mỗi câu phải có ý chính, ví dụ cụ thể trong Ebook2LateX và kết luận rõ. Tránh trả lời kiểu liệt kê tên công nghệ nhưng không nói vai trò.
| Mảng kiến thức | Biết | Hiểu | Áp dụng |
|---|---|---|---|
| Yêu cầu và công nghệ | Nêu FR1, FR2, FR3; FastAPI, React, PostgreSQL. | Giải thích vai trò PDF, LaTeX, MathLive. | Chọn Web/Desktop, CSR/SSR theo bối cảnh. |
| Phân tích hệ thống | Nhận diện thư mục api, core, services, schemas. | Phân biệt PyMuPDF, pix2tex/Mathpix, Pydantic. | Vẽ luồng PDF -> OCR -> LaTeX -> UI -> DB. |
| Git, GitHub, môi trường | Nêu git init, add, commit, remote, push. | Giải thích .gitignore, .env, upstream. | Xử lý tình huống lỡ stage .env hoặc push lần đầu lỗi. |
| Database và migration | Nêu PostgreSQL, DATABASE_URL, venv, requirements. | Giải thích ORM, Base.metadata, Alembic, cascade. | Thiết kế bảng, viết trình tự migration và seed. |
| Web services và FastAPI | Nêu client, server, request, response, JSON, REST. | Giải thích Uvicorn, ASGI, decorator, Pydantic. | Thiết kế endpoint upload/save hoặc route nhận tham số. |
| Học thời AI | Nêu kiến thức nền, bằng cấp, output, tư duy. | Giải thích vì sao AI không thay thế nền tảng. | Liên hệ cách học môn này và năng lực nghề nghiệp. |
Các bài có code/lệnh cần học kỹ: bài 2-3 về cấu trúc và Docker, bài 4 về Git, bài 5 về DATABASE_URL, bài 7 về venv/Alembic/models.py, bài 8 về database.py/seed.py, bài 9-10 về FastAPI/Uvicorn.
Học để biết mỗi phần code đặt ở đâu và dịch vụ nào chạy phần nào.
Ebook2LateX/
docker-compose.yml
backend/
Dockerfile
app/
main.py
api/
core/
models/
services/
schemas/
frontend/
Dockerfile
src/
components/
hooks/
services/
Học để khởi tạo repo, commit và đẩy code đúng trình tự.
git -v
git init
git status
git add .
git commit -m "Initial commit"
git log
git remote add origin <URL>
git branch -M main
git push -u origin main
Học để tự cấu hình backend kết nối database.
# .env
DATABASE_URL=postgresql://postgres:mat_khau_cua_ban@localhost:5432/ebook2latex_db
db.Học để tạo môi trường Python và migration database.
python -V
python -m venv venv
venv\Scripts\activate
pip install sqlalchemy alembic psycopg2-binary python-dotenv
pip freeze > requirements.txt
alembic init migrations
from models import Base
target_metadata = Base.metadata
alembic revision --autogenerate -m "Tao cac bang ban dau"
alembic upgrade head
Học để hiểu bảng, khóa ngoại, relationship, cascade và JSONB.
import uuid
from sqlalchemy import Column, String, Integer, ForeignKey, DateTime, Boolean, Text, Numeric
from sqlalchemy.dialects.postgresql import UUID, JSONB
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
Base = declarative_base()
class User(Base):
__tablename__ = "users"
user_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
username_email = Column(String(255), unique=True, nullable=False)
password_hash = Column(Text, nullable=False)
role = Column(String(20), default="Editor")
documents = relationship("Document", back_populates="owner")
class Document(Base):
__tablename__ = "documents"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), ForeignKey("users.user_id", ondelete="SET NULL"))
file_name = Column(Text, nullable=False)
file_path_url = Column(Text, nullable=False)
status = Column(String(50), default="Pending")
owner = relationship("User", back_populates="documents")
formulas = relationship("FormulaEntry", back_populates="document", cascade="all, delete-orphan")
class FormulaEntry(Base):
__tablename__ = "formula_entries"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
document_id = Column(UUID(as_uuid=True), ForeignKey("documents.id", ondelete="CASCADE"), nullable=False)
latex_content = Column(Text)
order_index = Column(Integer, nullable=False)
document = relationship("Document", back_populates="formulas")
Học để mở Session, thêm dữ liệu, commit, rollback và đóng kết nối.
# app/database.py
import os
from dotenv import load_dotenv
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
load_dotenv()
SQLALCHEMY_DATABASE_URL = os.getenv("DATABASE_URL")
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
# seed.py
import uuid
from app.database import SessionLocal
from app.models import User, Document, FormulaEntry
def seed_data():
db = SessionLocal()
try:
test_user = User(user_id=uuid.uuid4(), username_email="teo@dalat.edu.vn",
password_hash="hashed_password_here", role="Admin")
db.add(test_user)
db.flush()
test_doc = Document(id=uuid.uuid4(), user_id=test_user.user_id,
file_name="Giao_trinh_Toan_12.pdf",
file_path_url="/uploads/toan12.pdf", status="Completed")
db.add(test_doc)
db.flush()
formula = FormulaEntry(id=uuid.uuid4(), document_id=test_doc.id,
latex_content=r"\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}",
order_index=1)
db.add(formula)
db.commit()
except Exception:
db.rollback()
raise
finally:
db.close()
Học để viết service nhận request và trả JSON response.
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Chao mung ban den voi Ebook2LateX!"}
uvicorn main:app --reload
Phần này giải thích các block code/lệnh ở trên theo đúng cách nên trả lời trong phòng thi: nói code làm gì, vì sao cần, và lỗi dễ gặp.
services là web services. Trong cấu trúc thư mục, services là nơi chứa logic nghiệp vụ; còn web services là API chạy qua HTTP.git -v: Kiểm tra máy đã cài Git chưa. Nếu có version thì Git sẵn sàng.git init: Biến thư mục hiện tại thành Git repository, tạo thư mục ẩn .git.git status: Xem file nào mới, file nào đã sửa, file nào đang staged. Trước khi commit nên luôn xem lệnh này.git add .: Đưa thay đổi vào staging area. Chưa phải commit, chỉ là chọn file chuẩn bị lưu.git commit -m "...": Ghi một mốc lịch sử. Message nên nói rõ đã làm gì.git remote add origin <URL>: Gắn repo GitHub với repo local bằng tên tắt origin.git branch -M main: Đặt nhánh hiện tại thành main.git push -u origin main: Đẩy commit lên GitHub và thiết lập upstream để những lần sau push/pull dễ hơn..env, venv, node_modules, file upload tạm.git add .env. Cách trả lời: bỏ file khỏi staging, thêm .env vào .gitignore, kiểm tra lại git status.postgresql://user:password@host:port/database_name.postgresql://postgres:123456@localhost:5432/ebook2latex_db.postgres đầu tiên: Tên user mặc định của PostgreSQL.123456: Mật khẩu của user PostgreSQL.localhost: Database đang chạy trên máy local. Nếu chạy bằng Docker Compose, host có thể là tên service như db.5432: Cổng mặc định PostgreSQL.ebook2latex_db: Tên database dự án.localhost trong container khi đáng ra phải dùng db.python -m venv venv: Tạo môi trường Python riêng cho dự án để thư viện không xung đột với máy hoặc dự án khác.venv\Scripts\activate: Kích hoạt môi trường ảo trên Windows. Sau đó pip install sẽ cài vào venv.pip install sqlalchemy alembic psycopg2-binary python-dotenv: Cài ORM, migration, driver PostgreSQL và thư viện đọc .env.pip freeze > requirements.txt: Ghi lại danh sách thư viện và version để người khác/Docker cài lại giống mình.alembic init migrations: Khởi tạo Alembic, tạo thư mục migrations và file alembic.ini.from models import Base: Import Base chứa metadata của các model SQLAlchemy.target_metadata = Base.metadata: Cho Alembic biết schema trong code Python để so sánh với database thật.alembic revision --autogenerate: Tạo file migration dựa trên khác biệt giữa model và database.alembic upgrade head: Thực thi migration mới nhất vào PostgreSQL.Base = declarative_base(): Tạo lớp nền để các model kế thừa. Model kế thừa Base sẽ được SQLAlchemy ghi vào metadata.class User(Base): Bảng người dùng. Có email, password_hash, role. Không lưu mật khẩu thô.class Document(Base): Bảng tài liệu PDF. Lưu file_name, file_path_url, status và user_id của người upload.class FormulaEntry(Base): Bảng từng công thức. Lưu document_id, latex_content, order_index.UUID: ID dạng duy nhất, tránh trùng khi nhiều máy/người tạo dữ liệu.ForeignKey: Khóa ngoại để nối bảng. Ví dụ FormulaEntry có document_id trỏ về Documents.relationship: Giúp truy xuất đối tượng dễ hơn. Ví dụ document.formulas lấy các công thức thuộc tài liệu đó.cascade="all, delete-orphan": Xóa document thì công thức con bị xóa theo, tránh dữ liệu mồ côi.server_default=func.now(): Tự lấy thời điểm hiện tại khi tạo bản ghi.JSONB trong Logs: Dùng lưu thông tin môi trường linh hoạt như CPU/GPU/container.load_dotenv(): Đọc file .env để Python lấy được DATABASE_URL.os.getenv("DATABASE_URL"): Lấy chuỗi kết nối PostgreSQL từ biến môi trường.create_engine(...): Tạo engine, trung tâm quản lý kết nối tới database.SessionLocal = sessionmaker(...): Tạo factory sinh ra các phiên làm việc database.db = SessionLocal(): Mở một phiên làm việc để thêm/sửa/truy vấn dữ liệu.db.add(...): Đưa object mới vào session.db.flush(): Đẩy tạm dữ liệu để lấy ID cho bản ghi liên quan, nhưng chưa commit chính thức.db.commit(): Xác nhận lưu toàn bộ thay đổi.db.rollback(): Nếu lỗi, hoàn tác thay đổi chưa commit để tránh dữ liệu nửa vời.db.close(): Luôn đóng session để không rò rỉ kết nối.from fastapi import FastAPI: Import framework để tạo ứng dụng backend API.app = FastAPI(): Tạo đối tượng app. Uvicorn sẽ chạy biến này.@app.get("/"): Decorator khai báo route GET ở đường dẫn gốc /.def read_root(): Hàm xử lý request khi client truy cập route đó.return {"message": "..."}: Trả dict Python; FastAPI tự chuyển thành JSON response.uvicorn main:app --reload: Chạy file main.py, biến app. --reload tự khởi động lại khi sửa code.api_service.py và biến là my_web_app, lệnh đúng là uvicorn api_service:my_web_app.Các đề dưới đây không hỏi học thuộc. Mục tiêu là đọc code, sửa lỗi, viết thêm đoạn thiếu và giải thích vì sao chạy được.
postgresql://postgres:p@ssword1@localhost:5432/ebook2latex_db. Phân tích lỗi có thể xảy ra khi đặt trong alembic.ini và cách sửa.alembic revision --autogenerate nhưng file migration không tạo bảng nào. Hãy nêu ít nhất 3 nguyên nhân có thể.# migrations/env.py
target_metadata = None
alembic upgrade head.Log có các cột: log_id, formula_id, processing_time_ms, confidence_score, error_type, error_message, environment_info.FormulaEntry và Log, có cascade để xóa công thức thì log liên quan bị xóa.UserFavorites để user đánh dấu công thức yêu thích. Nêu khóa chính, khóa ngoại và unique constraint nên có.Documents.user_id dùng ondelete="SET NULL" còn FormulaEntries.document_id dùng ondelete="CASCADE".db.flush() sau khi thêm user và document trước khi thêm bản ghi phụ thuộc.db = SessionLocal()
test_doc = Document(file_name="a.pdf")
db.add(test_doc)
raise Exception("loi")
db.commit()
FormulaEntries với order_index tăng dần.python seed.py nhiều lần.GET /multiply/{number} nhận số và trả JSON chứa kết quả nhân 10.GET /shoe nhận query brand và size, trả câu xác nhận theo bài 10.POST /upload cho PDF: nêu input, output JSON và bước xử lý nội bộ.POST /formulas/save để lưu LaTeX đã sửa. Nêu schema request và response.Đạt điểm khi nêu đúng khái niệm, lệnh, vai trò. Mất điểm nếu chỉ ghi tên công cụ nhưng không nói dùng để làm gì.
Đạt điểm khi giải thích được quan hệ nguyên nhân: vì sao cần công cụ đó, nó nằm ở tầng nào, khác công cụ gần giống ra sao.
Đạt điểm khi xử lý được tình huống, có trình tự, có dữ liệu vào/ra, có lưu ý lỗi thường gặp và liên hệ đúng với Ebook2LateX.
DATABASE_URL=postgresql://postgres:123456@localhost:5432/ebook2latex_db. postgres là user, 123456 là mật khẩu, localhost là host, 5432 là port PostgreSQL, ebook2latex_db là database.python -m venv venv tạo môi trường ảo. venv\Scripts\activate kích hoạt. pip freeze > requirements.txt ghi package. alembic upgrade head áp dụng migration mới nhất.Base.metadata chứa bản thiết kế bảng/cột/quan hệ từ SQLAlchemy model. Alembic cần nó để so sánh model Python với database thật khi autogenerate migration.alembic revision --autogenerate tạo file migration dự thảo. alembic upgrade head thực thi file migration đó vào database.alembic.ini có thể phải viết p%%40ssword1. Trong chuỗi .env Python thường dùng p%40ssword1. Nhầm hai kiểu này dễ làm kết nối lỗi.version = Column(Integer, default=1). Chạy alembic revision --autogenerate -m "add version to documents". Kiểm tra file migration không drop bảng/cột cũ. Chạy alembic upgrade head. Kiểm tra dữ liệu cũ vẫn còn và version có giá trị mặc định.POST /upload nhận file PDF/form-data, trả JSON danh sách công thức LaTeX và metadata. POST /formulas/save nhận document_id và danh sách latex_content đã sửa, lưu PostgreSQL, trả status hoặc ids đã lưu.127.0.0.1:8000. Uvicorn lắng nghe cổng và chuyển request vào FastAPI. FastAPI match route qua decorator, gọi hàm xử lý, có thể validate bằng Pydantic và thao tác database. Sau đó FastAPI trả dict/JSON, Uvicorn gửi HTTP response về client.@ phân tách phần user/password với host. Mật khẩu p@ssword1 có thể làm parser hiểu sai. Trong alembic.ini sửa thành postgresql://postgres:p%%40ssword1@localhost:5432/ebook2latex_db. Trong .env Python thường dùng p%40ssword1.target_metadata = None, chưa import đúng Base, model không kế thừa đúng Base, Alembic không kết nối đúng database, hoặc database đã có sẵn schema giống model.from models import Base
target_metadata = Base.metadata
Nếu model nằm trong package, cần import đúng đường dẫn, ví dụ from app.models import Base.
python -m venv venv -> venv\Scripts\activate -> pip install sqlalchemy alembic psycopg2-binary python-dotenv -> pip freeze > requirements.txt -> alembic init migrations -> cấu hình URL và metadata -> alembic revision --autogenerate -m "..." -> alembic upgrade head.class Log(Base):
__tablename__ = "logs"
log_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
formula_id = Column(UUID(as_uuid=True), ForeignKey("formula_entries.id", ondelete="CASCADE"))
processing_time_ms = Column(Integer)
confidence_score = Column(Numeric(3, 2))
error_type = Column(String(100))
error_message = Column(Text)
timestamp = Column(DateTime(timezone=True), server_default=func.now())
environment_info = Column(JSONB)
formula = relationship("FormulaEntry", back_populates="logs")
FormulaEntry thêm logs = relationship("Log", back_populates="formula", cascade="all, delete-orphan"). Khi xóa công thức, log mồ côi cũng bị xóa.class UserFavorite(Base):
__tablename__ = "user_favorites"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), ForeignKey("users.user_id", ondelete="CASCADE"), nullable=False)
formula_id = Column(UUID(as_uuid=True), ForeignKey("formula_entries.id", ondelete="CASCADE"), nullable=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
Nên thêm unique constraint cho cặp user_id, formula_id để không đánh dấu trùng.
SET NULL. Nếu xóa document thì công thức thuộc document đó không còn ngữ cảnh, nên dùng CASCADE.alembic revision --autogenerate -m "update models", kiểm tra migration, chạy alembic upgrade head, kiểm tra bảng/khóa ngoại trong pgAdmin hoặc truy vấn.db = SessionLocal()
try:
user = User(user_id=uuid.uuid4(), username_email="demo@dalat.edu.vn",
password_hash="hashed", role="Admin")
db.add(user)
db.flush()
doc = Document(id=uuid.uuid4(), user_id=user.user_id,
file_name="toan.pdf", file_path_url="/uploads/toan.pdf",
status="Completed")
db.add(doc)
db.flush()
formulas = [r"x^2+1", r"\sqrt{x}", r"\frac{a}{b}"]
for index, latex in enumerate(formulas, start=1):
db.add(FormulaEntry(id=uuid.uuid4(), document_id=doc.id,
latex_content=latex, order_index=index))
db.commit()
except Exception:
db.rollback()
raise
finally:
db.close()
flush() đẩy tạm dữ liệu vào session/database để lấy khóa chính user_id và doc.id cho bản ghi có khóa ngoại, nhưng chưa commit toàn bộ transaction.file_path_url, thiếu user/document quan hệ nếu model yêu cầu. Cần đặt commit trước khi kết thúc thành công và rollback khi lỗi.enumerate(list_formula, start=1), mỗi phần tử tạo một FormulaEntry với cùng document_id, latex_content là chuỗi, order_index tăng dần.@app.get("/multiply/{number}")
def multiply(number: int):
return {"number": number, "result": number * 10}
@app.get("/shoe")
def shoe(brand: str, size: int):
return {"message": f"Bạn muốn mua giày {brand} kích thước {size} đúng không?"}
{"document_id": "...", "formulas": [{"order_index": 1, "latex": "..."}]}.document_id và danh sách formulas gồm latex_content, order_index. Response trả status, số công thức đã lưu hoặc ids đã tạo.latexContent và danh sách công thức -> axios gửi POST tới FastAPI -> Pydantic kiểm tra request -> FastAPI mở SessionLocal -> SQLAlchemy tạo/cập nhật FormulaEntries -> PostgreSQL lưu dữ liệu -> backend commit và trả JSON response cho frontend.