Ôn tự luận cho 20/05/2026

Bộ đề thi thực tế môn Ebook2LateX

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.

Quay lại trắc nghiệm

Kế Hoạch Nghiên Cứu Sâu

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.

2. Học theo chuỗi nguyên nhân

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ì.

3. Luyện theo 3 mức

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.

4. Chấm như thi thật

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ò.

Ma Trận Đề

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.

Lịch Ôn Cấp Tốc

  1. 90 phút đầu: học lại bài 1-3, viết một sơ đồ tổng thể Ebook2LateX từ PDF tới database.
  2. 90 phút tiếp: học bài 4-8, viết lại bằng tay chuỗi Git, DATABASE_URL, Alembic, SQLAlchemy model, seed.py.
  3. 60 phút tiếp: học bài 9-10, tự giải thích client-server, web services, FastAPI route và Uvicorn.
  4. 45 phút tiếp: học bài 11, chuẩn bị 1 đoạn liên hệ AI, kiến thức nền, output và cách học.
  5. 120 phút cuối: làm Đề 3 hoặc Đề 4 như thi thật, sau đó rà đáp án theo barem bên dưới.

Phần Riêng Để Học Code

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.

Bài 2-3: Cấu trúc dự án và Docker

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/
  • api: endpoint upload, process, save.
  • services: xử lý PDF và OCR.
  • schemas: Pydantic validation.
  • docker-compose.yml: điều phối db, backend, frontend.

Bài 4: Git và GitHub

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
  • .gitignore: loại node_modules, venv, .env, uploads tạm.
  • -u: thiết lập upstream cho nhánh main.

Bài 5: Chuỗi kết nối PostgreSQL

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
  • postgres: user mặc định.
  • localhost: máy local; trong Docker có thể là db.
  • 5432: port mặc định PostgreSQL.
  • ebook2latex_db: database của dự án.

Bài 7: venv, requirements và Alembic

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

Bài 7: models.py cốt lõi

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")

Bài 8: database.py và seed.py

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()

Bài 9-10: FastAPI và Uvicorn

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
  • main: tên file main.py.
  • app: biến app = FastAPI().
  • --reload: tự chạy lại khi sửa code.

Đáp Án Học Code Dễ Hiểu

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.

Bài 2-3: Cấu trúc dự án và Docker
  1. Ý chính: Dự án được chia thành backend, frontend và database để mỗi phần làm đúng trách nhiệm. Backend xử lý PDF/OCR/API, frontend hiển thị và cho sửa công thức, database lưu dữ liệu.
  2. backend/app/api: Nơi viết các endpoint như upload PDF, xử lý OCR, lưu LaTeX. Khi frontend gọi API thì request sẽ đi vào đây.
  3. backend/app/services: Nơi đặt logic nặng như đọc PDF bằng PyMuPDF, cắt vùng công thức, gọi pix2tex/Mathpix. Không nên nhét logic này trực tiếp vào route vì route sẽ rối.
  4. backend/app/schemas: Nơi khai báo Pydantic schema để kiểm tra dữ liệu vào/ra. Ví dụ request lưu công thức phải có document_id và latex_content.
  5. Dockerfile: Công thức đóng gói một service. Backend có Dockerfile riêng, frontend có Dockerfile riêng.
  6. docker-compose.yml: File điều phối nhiều service cùng chạy: db, backend, frontend. Trả lời dễ nhớ: Dockerfile tạo từng “nhạc cụ”, compose là “nhạc trưởng”.
  7. Lỗi hay gặp: Nhầm 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.
Bài 4: Git và GitHub
  1. git -v: Kiểm tra máy đã cài Git chưa. Nếu có version thì Git sẵn sàng.
  2. git init: Biến thư mục hiện tại thành Git repository, tạo thư mục ẩn .git.
  3. 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.
  4. git add .: Đưa thay đổi vào staging area. Chưa phải commit, chỉ là chọn file chuẩn bị lưu.
  5. git commit -m "...": Ghi một mốc lịch sử. Message nên nói rõ đã làm gì.
  6. git remote add origin <URL>: Gắn repo GitHub với repo local bằng tên tắt origin.
  7. git branch -M main: Đặt nhánh hiện tại thành main.
  8. 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.
  9. .gitignore: Dùng để không đưa file rác hoặc nhạy cảm vào Git như .env, venv, node_modules, file upload tạm.
  10. Lỗi hay gặp: Lỡ 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.
Bài 5: DATABASE_URL PostgreSQL
  1. Cấu trúc: postgresql://user:password@host:port/database_name.
  2. Ví dụ: postgresql://postgres:123456@localhost:5432/ebook2latex_db.
  3. postgres đầu tiên: Tên user mặc định của PostgreSQL.
  4. 123456: Mật khẩu của user PostgreSQL.
  5. 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.
  6. 5432: Cổng mặc định PostgreSQL.
  7. ebook2latex_db: Tên database dự án.
  8. Vì sao để trong .env: Để không viết cứng password vào code, dễ đổi môi trường local/Docker/cloud, và tránh lộ thông tin khi push Git.
  9. Lỗi hay gặp: Đảo user/password, dùng nhầm port 8000 của FastAPI thay vì 5432 của PostgreSQL, hoặc dùng localhost trong container khi đáng ra phải dùng db.
Bài 7: venv, requirements và Alembic
  1. 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.
  2. venv\Scripts\activate: Kích hoạt môi trường ảo trên Windows. Sau đó pip install sẽ cài vào venv.
  3. pip install sqlalchemy alembic psycopg2-binary python-dotenv: Cài ORM, migration, driver PostgreSQL và thư viện đọc .env.
  4. 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.
  5. alembic init migrations: Khởi tạo Alembic, tạo thư mục migrations và file alembic.ini.
  6. from models import Base: Import Base chứa metadata của các model SQLAlchemy.
  7. target_metadata = Base.metadata: Cho Alembic biết schema trong code Python để so sánh với database thật.
  8. alembic revision --autogenerate: Tạo file migration dựa trên khác biệt giữa model và database.
  9. alembic upgrade head: Thực thi migration mới nhất vào PostgreSQL.
  10. Lỗi hay gặp: Migration rỗng vì import sai Base, model không kế thừa Base, hoặc target_metadata vẫn là None.
Bài 7: models.py
  1. 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.
  2. class User(Base): Bảng người dùng. Có email, password_hash, role. Không lưu mật khẩu thô.
  3. 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.
  4. class FormulaEntry(Base): Bảng từng công thức. Lưu document_id, latex_content, order_index.
  5. UUID: ID dạng duy nhất, tránh trùng khi nhiều máy/người tạo dữ liệu.
  6. ForeignKey: Khóa ngoại để nối bảng. Ví dụ FormulaEntry có document_id trỏ về Documents.
  7. 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 đó.
  8. 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.
  9. server_default=func.now(): Tự lấy thời điểm hiện tại khi tạo bản ghi.
  10. JSONB trong Logs: Dùng lưu thông tin môi trường linh hoạt như CPU/GPU/container.
Bài 8: database.py và seed.py
  1. load_dotenv(): Đọc file .env để Python lấy được DATABASE_URL.
  2. os.getenv("DATABASE_URL"): Lấy chuỗi kết nối PostgreSQL từ biến môi trường.
  3. create_engine(...): Tạo engine, trung tâm quản lý kết nối tới database.
  4. SessionLocal = sessionmaker(...): Tạo factory sinh ra các phiên làm việc database.
  5. db = SessionLocal(): Mở một phiên làm việc để thêm/sửa/truy vấn dữ liệu.
  6. db.add(...): Đưa object mới vào session.
  7. 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.
  8. db.commit(): Xác nhận lưu toàn bộ thay đổi.
  9. db.rollback(): Nếu lỗi, hoàn tác thay đổi chưa commit để tránh dữ liệu nửa vời.
  10. db.close(): Luôn đóng session để không rò rỉ kết nối.
  11. Lỗi hay gặp: Quên rollback khi lỗi, quên close session, thiếu flush nên chưa có ID để gán khóa ngoại, hoặc chạy seed nhiều lần làm trùng dữ liệu.
Bài 9-10: FastAPI và Uvicorn
  1. from fastapi import FastAPI: Import framework để tạo ứng dụng backend API.
  2. app = FastAPI(): Tạo đối tượng app. Uvicorn sẽ chạy biến này.
  3. @app.get("/"): Decorator khai báo route GET ở đường dẫn gốc /.
  4. def read_root(): Hàm xử lý request khi client truy cập route đó.
  5. return {"message": "..."}: Trả dict Python; FastAPI tự chuyển thành JSON response.
  6. uvicorn main:app --reload: Chạy file main.py, biến app. --reload tự khởi động lại khi sửa code.
  7. Client-server: Client gửi request, server xử lý và trả response. Trong Ebook2LateX, React là client, FastAPI là server/provider.
  8. JSON: Dữ liệu có cấu trúc để frontend/chương trình đọc, khác với HTML chủ yếu để người xem.
  9. Lỗi hay gặp: Chạy sai cú pháp Uvicorn. Nếu file là api_service.py và biến là my_web_app, lệnh đúng là uvicorn api_service:my_web_app.

6 Bộ Đề Tự Luận

Đề 1 - Cơ Bản, 60 Phút

10 điểm
  1. Biết - 1.0đ: Trình bày ngắn gọn FR1, FR2, FR3 của hệ thống Ebook2LateX.
  2. Biết - 1.0đ: Nêu vai trò của FastAPI, React và PostgreSQL trong công nghệ được chọn.
  3. Hiểu - 1.5đ: Giải thích vì sao công thức toán nên được chuyển từ ảnh/PDF sang LaTeX thay vì chỉ lưu ảnh.
  4. Hiểu - 1.5đ: Phân biệt vai trò của PyMuPDF, pix2tex/Mathpix và MathLive.
  5. Áp dụng - 2.0đ: Mô tả luồng xử lý khi người dùng tải PDF lên, nhận công thức LaTeX, chỉnh sửa và bấm Submit.
  6. Áp dụng - 3.0đ: Một bạn nói: “Chỉ cần frontend React là đủ, không cần backend và database”. Hãy phản biện dựa trên FR1-FR3.

Đề 2 - Cơ Bản Mở Rộng, 75 Phút

10 điểm
  1. Biết - 1.0đ: Nêu chức năng của các thư mục backend/app/api, backend/app/services, backend/app/schemas.
  2. Biết - 1.0đ: Liệt kê các lệnh Git chính để tạo commit đầu tiên và đẩy lên GitHub.
  3. Hiểu - 1.5đ: Giải thích vì sao file .env không nên đưa lên GitHub.
  4. Hiểu - 1.5đ: So sánh Dockerfile và docker-compose.yml trong dự án có db, backend, frontend.
  5. Áp dụng - 2.0đ: Viết chuỗi DATABASE_URL cho PostgreSQL với user postgres, mật khẩu 123456, host localhost, port 5432, database ebook2latex_db. Giải thích từng phần.
  6. Áp dụng - 3.0đ: Nếu một thành viên mới clone dự án nhưng chạy không được vì thiếu môi trường, bạn sẽ dùng những file/công cụ nào để giúp họ chạy đồng bộ?

Đề 3 - Trung Bình, 75 Phút

10 điểm
  1. Biết - 1.0đ: Nêu bốn bảng chính trong thiết kế database Ebook2LateX và vai trò từng bảng.
  2. Biết - 1.0đ: Nêu ý nghĩa các trường DocumentID, LatexContent, OrderIndex trong FormulaEntries.
  3. Hiểu - 1.5đ: Vì sao Documents và FormulaEntries là quan hệ một-nhiều? Cho ví dụ.
  4. Hiểu - 1.5đ: Giải thích vai trò của Logs trong việc theo dõi OCR AI. ConfidenceScore và ErrorType giúp gì?
  5. Áp dụng - 2.0đ: Đề xuất cách lưu một tài liệu PDF có 5 công thức đã OCR vào các bảng Documents và FormulaEntries.
  6. Áp dụng - 3.0đ: Thiết kế thêm tính năng người dùng đánh dấu công thức yêu thích. Nêu bảng mới cần có, khóa ngoại và lý do.

Đề 4 - Thực Hành Hệ Thống, 90 Phút

10 điểm
  1. Biết - 1.0đ: Nêu venv, requirements.txt, SQLAlchemy, Alembic dùng để làm gì.
  2. Biết - 1.0đ: Nêu các lệnh cơ bản: python -m venv venv, venv\Scripts\activate, pip freeze > requirements.txt, alembic upgrade head.
  3. Hiểu - 1.5đ: Giải thích vì sao target_metadata = Base.metadata là bước quan trọng trong Alembic.
  4. Hiểu - 1.5đ: Phân biệt alembic revision --autogenerate và alembic upgrade head.
  5. Áp dụng - 2.0đ: Một mật khẩu PostgreSQL là p@ssword1. Giải thích vì sao trong alembic.ini có thể cần p%%40ssword1, còn trong .env Python thường dùng p%40ssword1.
  6. Áp dụng - 3.0đ: Trình bày quy trình an toàn để thêm cột version vào bảng Documents mà không làm mất dữ liệu cũ.

Đề 5 - Nâng Cao, 90 Phút

10 điểm
  1. Biết - 1.0đ: Nêu khái niệm web services, service consumer, service provider, HTTP request, HTTP response.
  2. Biết - 1.0đ: Nêu vai trò của JSON/XML, REST, FastAPI, Uvicorn, Pydantic.
  3. Hiểu - 1.5đ: Phân biệt trang web dành cho người đọc và web service dành cho chương trình xử lý.
  4. Hiểu - 1.5đ: Giải thích vì sao FastAPI phù hợp để làm backend web services hiện đại.
  5. Áp dụng - 2.0đ: Thiết kế hai endpoint cho Ebook2LateX: một endpoint upload PDF để OCR, một endpoint save để lưu LaTeX. Nêu method, dữ liệu vào, dữ liệu ra.
  6. Áp dụng - 3.0đ: Phân tích toàn bộ luồng client-server khi trình duyệt gửi request tới Uvicorn tại cổng 8000 và FastAPI trả JSON về.

Đề 6 - Tổng Hợp Mô Phỏng Thi Thật, 90 Phút

10 điểm
  1. Biết - 1.0đ: Lập bảng 6 công cụ: Git, Docker, PostgreSQL, SQLAlchemy, Alembic, FastAPI. Mỗi công cụ ghi 1 vai trò.
  2. Biết - 1.0đ: Nêu 3 năng lực học thời AI mà sinh viên cần ngoài kiến thức chuyên môn.
  3. Hiểu - 1.5đ: Giải thích vì sao “biết tên công cụ” chưa đủ để làm được dự án Ebook2LateX.
  4. Hiểu - 1.5đ: Phân tích câu: “AI hỗ trợ truy xuất thông tin nhưng không thay thế kiến thức nền và tư duy phản biện”.
  5. Áp dụng - 2.0đ: Một hệ thống OCR trả kết quả sai nhiều và chậm. Hãy đề xuất cách dùng Logs, ConfidenceScore, ProcessingTime, ErrorMessage để tìm nguyên nhân.
  6. Áp dụng - 3.0đ: Viết một bài ngắn 250-350 chữ: nếu bạn là thành viên nhóm xây dựng Ebook2LateX, bạn sẽ tổ chức quy trình từ yêu cầu đến triển khai và kiểm thử như thế nào?

Đề Vận Dụng Code Nâng Cao

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.

Đề Code 7 - Debug Kết Nối Và Migration, 90 Phút

10 điểm
  1. 2.0đ: Cho chuỗi 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.
  2. 2.0đ: Một bạn chạy 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ể.
  3. 2.0đ: Sửa đoạn cấu hình Alembic sau để nó thấy model:
    # migrations/env.py
    target_metadata = None
  4. 2.0đ: Viết đúng trình tự lệnh từ lúc tạo venv tới lúc tạo bảng trong PostgreSQL.
  5. 2.0đ: Giải thích vì sao phải kiểm tra file migration trước khi chạy alembic upgrade head.

Đề Code 8 - Hoàn Thiện Model Database, 90 Phút

10 điểm
  1. 2.0đ: Viết thêm class Log có các cột: log_id, formula_id, processing_time_ms, confidence_score, error_type, error_message, environment_info.
  2. 2.0đ: Thêm relationship hai chiều giữa FormulaEntryLog, có cascade để xóa công thức thì log liên quan bị xóa.
  3. 2.0đ: Thêm bảng 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ó.
  4. 2.0đ: Giải thích vì sao Documents.user_id dùng ondelete="SET NULL" còn FormulaEntries.document_id dùng ondelete="CASCADE".
  5. 2.0đ: Nêu các bước Alembic cần chạy sau khi sửa model.

Đề Code 9 - Viết Seed Thực Tế, 90 Phút

10 điểm
  1. 2.0đ: Viết logic seed tạo 1 user, 1 document và 3 formula entries cùng thuộc document đó.
  2. 2.0đ: Giải thích vì sao cần db.flush() sau khi thêm user và document trước khi thêm bản ghi phụ thuộc.
  3. 2.0đ: Sửa lỗi đoạn code sau:
    db = SessionLocal()
    test_doc = Document(file_name="a.pdf")
    db.add(test_doc)
    raise Exception("loi")
    db.commit()
  4. 2.0đ: Viết cách đọc 10 công thức từ danh sách Python và seed vào FormulaEntries với order_index tăng dần.
  5. 2.0đ: Nêu cách tránh seed trùng dữ liệu khi chạy python seed.py nhiều lần.

Đề Code 10 - Thiết Kế FastAPI Cho Ebook2LateX, 90 Phút

10 điểm
  1. 2.0đ: Viết endpoint GET /multiply/{number} nhận số và trả JSON chứa kết quả nhân 10.
  2. 2.0đ: Viết endpoint GET /shoe nhận query brandsize, trả câu xác nhận theo bài 10.
  3. 2.0đ: Thiết kế endpoint POST /upload cho PDF: nêu input, output JSON và bước xử lý nội bộ.
  4. 2.0đ: Thiết kế endpoint POST /formulas/save để lưu LaTeX đã sửa. Nêu schema request và response.
  5. 2.0đ: Giải thích luồng request từ React tới FastAPI tới SQLAlchemy rồi PostgreSQL khi bấm Submit.

Barem Chấm Nhanh

Mức Biết

Đạ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ì.

Mức Hiểu

Đạ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.

Mức Áp Dụng

Đạ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.

Đáp Án Tự Luận Theo Từng Đề

Đề 1 - Cơ bản
  1. FR1, FR2, FR3: FR1 là hệ thống đọc PDF toán học, trích vùng công thức và OCR ra LaTeX. FR2 là giao diện cho phép xem/sửa công thức ở dạng LaTeX và MathLive, hai bên cập nhật hai chiều. FR3 là khi bấm Submit, nội dung LaTeX cuối cùng được lưu xuống database.
  2. FastAPI, React, PostgreSQL: FastAPI làm backend/API xử lý upload, OCR, save. React làm frontend, quản lý state và giao diện MathLive. PostgreSQL lưu tài liệu, công thức, người dùng, log.
  3. Vì sao cần LaTeX: Ảnh công thức khó sửa, khó tìm kiếm và khó tái sử dụng. LaTeX là văn bản, có thể lưu, chỉnh sửa, đồng bộ với MathLive và dùng lại trong tài liệu/toán học.
  4. PyMuPDF, pix2tex/Mathpix, MathLive: PyMuPDF đọc PDF và trích ảnh/vùng công thức. pix2tex hoặc Mathpix chuyển ảnh công thức thành LaTeX. MathLive hiển thị và cho sửa công thức trực quan ở frontend.
  5. Luồng xử lý: Người dùng upload PDF -> backend nhận file -> PyMuPDF cắt vùng công thức -> OCR trả LaTeX -> frontend hiển thị trong textarea và MathLive -> người dùng sửa -> Submit -> FastAPI nhận LaTeX -> SQLAlchemy lưu vào PostgreSQL.
  6. Phản biện “chỉ cần React”: React chỉ xử lý giao diện và state phía client. FR1 cần backend/OCR để đọc PDF và xử lý mô hình. FR3 cần backend và database để lưu bền vững. Không có backend/database thì chỉ có thể nhập/sửa tạm, không hoàn thành hệ thống.
Đề 2 - Cơ bản mở rộng
  1. api, services, schemas: api chứa endpoint như upload/process/save. services chứa logic xử lý PDF, OCR, nghiệp vụ. schemas chứa Pydantic model để kiểm tra request/response.
  2. Lệnh Git: git init, git status, git add ., git commit -m "...", tạo repo GitHub, git remote add origin URL, git branch -M main, git push -u origin main.
  3. .env không đưa lên GitHub: .env có thể chứa DATABASE_URL, password, API key. Nếu commit lên GitHub sẽ lộ thông tin nhạy cảm. Cần thêm .env vào .gitignore.
  4. Dockerfile và compose: Dockerfile mô tả cách đóng gói một service thành image. docker-compose.yml điều phối nhiều service như db, backend, frontend chạy cùng mạng/cấu hình.
  5. DATABASE_URL: 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.
  6. Giúp thành viên mới chạy đồng bộ: Dùng README hướng dẫn, .gitignore để loại file không cần, requirements.txt để cài Python packages, package.json cho frontend, Dockerfile/docker-compose.yml để chạy db/backend/frontend giống nhau, .env.example nếu có để cấu hình local.
Đề 3 - Trung bình
  1. Bốn bảng: Documents lưu thông tin file PDF. FormulaEntries lưu từng công thức LaTeX. Users lưu tài khoản, quyền, trạng thái. Logs lưu hiệu suất/lỗi OCR.
  2. DocumentID, LatexContent, OrderIndex: DocumentID là khóa ngoại nối công thức với tài liệu. LatexContent là mã LaTeX OCR hoặc đã sửa. OrderIndex giữ thứ tự công thức trong tài liệu.
  3. Quan hệ một-nhiều: Một tài liệu PDF có thể có nhiều công thức. Vì vậy một dòng Documents liên kết với nhiều dòng FormulaEntries qua DocumentID.
  4. Logs: Logs giúp theo dõi model OCR: ProcessingTime đo thời gian, ConfidenceScore đo độ tin cậy, ErrorType phân loại lỗi, ErrorMessage giúp debug, EnvironmentInfo ghi môi trường chạy.
  5. Lưu PDF có 5 công thức: Tạo 1 bản ghi Documents gồm file_name, file_path_url, status. Tạo 5 bản ghi FormulaEntries có cùng document_id, mỗi dòng có latex_content, raw_image_path nếu có, order_index từ 1 đến 5.
  6. Tính năng yêu thích: Thêm bảng UserFavorites gồm id, user_id khóa ngoại tới Users, formula_id khóa ngoại tới FormulaEntries, created_at. Có thể thêm unique(user_id, formula_id) để một người không đánh dấu trùng một công thức.
Đề 4 - Thực hành hệ thống
  1. Khái niệm: venv cô lập Python/thư viện cho dự án. requirements.txt ghi phụ thuộc. SQLAlchemy là ORM định nghĩa model và thao tác database bằng Python. Alembic quản lý migration/schema theo phiên bản.
  2. Lệnh: 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.
  3. target_metadata: 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.
  4. revision và upgrade: alembic revision --autogenerate tạo file migration dự thảo. alembic upgrade head thực thi file migration đó vào database.
  5. Mã hóa @: Ký tự @ trong URL có nghĩa đặc biệt. Trong 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.
  6. Thêm cột version an toàn: Sửa model Document thêm 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.
Đề 5 - Nâng cao
  1. Khái niệm: Web services là dịch vụ phần mềm trao đổi qua web/HTTP. Service consumer là client gọi dịch vụ. Service provider là server cung cấp dịch vụ. HTTP request là yêu cầu gửi đi, HTTP response là kết quả trả về.
  2. Vai trò: JSON/XML là định dạng trao đổi dữ liệu. REST là kiểu thiết kế web service phổ biến. FastAPI tạo API backend. Uvicorn chạy app FastAPI theo ASGI. Pydantic kiểm tra và phân tích dữ liệu.
  3. Trang web vs web service: Trang web thường trả HTML/CSS/JS cho con người xem. Web service thường trả JSON/XML cho chương trình khác xử lý. Cả hai có thể dùng HTTP nhưng khác đối tượng sử dụng và dạng dữ liệu trả về.
  4. Vì sao FastAPI phù hợp: FastAPI nhanh, hỗ trợ async, dùng Pydantic validation, dễ viết endpoint RESTful, tự sinh tài liệu API, phù hợp backend nhận PDF, trả JSON LaTeX, lưu database.
  5. Endpoint đề xuất: 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.
  6. Luồng client-server: Trình duyệt/client gửi HTTP request tới 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.
Đề 6 - Tổng hợp mô phỏng thi thật
  1. 6 công cụ: Git quản lý phiên bản. Docker đóng gói môi trường. PostgreSQL lưu dữ liệu quan hệ. SQLAlchemy là ORM làm việc với database bằng Python. Alembic migration schema. FastAPI tạo backend API.
  2. 3 năng lực thời AI: Kiến thức nền để kiểm chứng AI, tư duy phản biện/hệ thống, khả năng tự học và thích nghi. Có thể bổ sung giao tiếp, teamwork, tạo output thực tế.
  3. Biết tên công cụ chưa đủ: Làm dự án cần biết công cụ nằm ở tầng nào, dữ liệu đi qua nó ra sao, giải quyết FR nào, kết nối với công cụ khác thế nào và lỗi thường gặp. Chỉ thuộc tên không thiết kế, debug hoặc triển khai được.
  4. AI và kiến thức nền: AI hỗ trợ truy xuất/tổng hợp nhưng có thể sai, thiếu ngữ cảnh hoặc tạo câu trả lời hợp lý giả. Người học cần nền tảng để đặt câu hỏi đúng, kiểm chứng, phản biện và chịu trách nhiệm quyết định.
  5. OCR sai/chậm: Dùng Logs để xem công thức nào lỗi. ConfidenceScore thấp cho thấy OCR không chắc. ProcessingTime cao chỉ điểm bước chậm hoặc tải nặng. ErrorMessage/ErrorType giúp phân loại timeout, invalid image, model crash. EnvironmentInfo giúp xem CPU/GPU/container có vấn đề không.
  6. Bài 250-350 chữ cần có: Mở bài nêu mục tiêu Ebook2LateX. Thân bài trình bày: phân tích FR1-FR3, chọn FastAPI/React/PostgreSQL, khởi tạo Git, thiết kế bảng, tạo ORM/migration, seed dữ liệu, xây Parse tool, tích hợp MathLive, tạo API save, kiểm thử PDF nhiều mức độ, Docker hóa. Kết bài nêu cách dùng AI như công cụ hỗ trợ nhưng vẫn cần hiểu hệ thống.

Đáp Án Đề Code Nâng Cao

Đề Code 7 - Debug kết nối và migration
  1. Chuỗi có @: Trong URL, ký tự @ 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.
  2. Autogenerate không tạo bảng: Có thể do 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.
  3. Sửa env.py:
    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.
  4. Trình tự lệnh: 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.
  5. Kiểm tra migration: Vì autogenerate có thể tạo lệnh sai hoặc nguy hiểm như drop cột/bảng. Cần xem file có đúng create/add/alter mong muốn, không làm mất dữ liệu cũ.
Đề Code 8 - Hoàn thiện model database
  1. Class Log mẫu:
    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")
  2. Relationship: Trong 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.
  3. UserFavorites:
    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.
  4. SET NULL và CASCADE: Nếu xóa user, tài liệu có thể vẫn cần giữ lại nên 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.
  5. Sau khi sửa model: Chạy 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.
Đề Code 9 - Viết seed thực tế
  1. Seed 1 user, 1 document, 3 formulas:
    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()
  2. Vì sao flush: flush() đẩy tạm dữ liệu vào session/database để lấy khóa chính user_iddoc.id cho bản ghi có khóa ngoại, nhưng chưa commit toàn bộ transaction.
  3. Sửa lỗi đoạn code: Thiếu try/except/finally, thiếu rollback, thiếu các trường bắt buộc như 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.
  4. Seed danh sách công thức: Dùng 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.
  5. Tránh trùng dữ liệu: Kiểm tra user/document đã tồn tại trước khi insert, dùng unique constraint, xóa dữ liệu seed cũ trước khi chạy lại trong môi trường dev, hoặc dùng id cố định cho dữ liệu mẫu.
Đề Code 10 - Thiết kế FastAPI
  1. GET /multiply/{number}:
    @app.get("/multiply/{number}")
    def multiply(number: int):
        return {"number": number, "result": number * 10}
  2. GET /shoe:
    @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?"}
  3. POST /upload: Input là PDF dạng multipart/form-data. Backend lưu tạm file, dùng PyMuPDF cắt vùng công thức, gọi pix2tex/Mathpix OCR, trả JSON như {"document_id": "...", "formulas": [{"order_index": 1, "latex": "..."}]}.
  4. POST /formulas/save: Request nên có 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.
  5. Luồng Submit: React giữ 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.

Gợi Ý Đáp Án Cốt Lõi

Những ý bắt buộc phải nhớ
  • FR1: đọc PDF toán học, trích/cắt công thức, OCR ra LaTeX.
  • FR2: giao diện React đồng bộ hai chiều giữa LaTeX raw và MathLive.
  • FR3: bấm Submit thì LaTeX cuối cùng được lưu xuống PostgreSQL qua backend.
  • PyMuPDF xử lý PDF; pix2tex/Mathpix OCR công thức; MathLive hiển thị và sửa công thức trực quan.
  • Documents lưu file PDF; FormulaEntries lưu từng công thức; Users quản lý người dùng; Logs theo dõi OCR.
  • SQLAlchemy định nghĩa model và làm ORM; Alembic quản lý thay đổi schema; Base.metadata giúp Alembic thấy model.
  • seed.py dùng SessionLocal, add, flush, commit, rollback, close để nạp dữ liệu mẫu an toàn.
  • Web service là dịch vụ qua HTTP; FastAPI cung cấp API; Uvicorn chạy app; JSON là dữ liệu trao đổi cho chương trình.
  • Thời AI: kiến thức nền, tư duy phản biện, khả năng học nhanh, output thực tế và trách nhiệm ra quyết định vẫn là lõi.