Prathamesh Sable commited on
Commit
724ff38
·
1 Parent(s): 70e1f4d

working models

Browse files
.gitignore CHANGED
@@ -1,2 +1,4 @@
1
  __pycache__
2
- venv
 
 
 
1
  __pycache__
2
+ venv
3
+ .env
4
+ .pytest_cache
routes/__init__.py → __init__.py RENAMED
File without changes
alembic.ini ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # A generic, single database configuration.
2
+
3
+ [alembic]
4
+ # path to migration scripts
5
+ # Use forward slashes (/) also on windows to provide an os agnostic path
6
+ script_location = migrations
7
+
8
+ # template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
9
+ # Uncomment the line below if you want the files to be prepended with date and time
10
+ # see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
11
+ # for all available tokens
12
+ # file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
13
+
14
+ # sys.path path, will be prepended to sys.path if present.
15
+ # defaults to the current working directory.
16
+ prepend_sys_path = .
17
+
18
+ # timezone to use when rendering the date within the migration file
19
+ # as well as the filename.
20
+ # If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library.
21
+ # Any required deps can installed by adding `alembic[tz]` to the pip requirements
22
+ # string value is passed to ZoneInfo()
23
+ # leave blank for localtime
24
+ # timezone =
25
+
26
+ # max length of characters to apply to the "slug" field
27
+ # truncate_slug_length = 40
28
+
29
+ # set to 'true' to run the environment during
30
+ # the 'revision' command, regardless of autogenerate
31
+ # revision_environment = false
32
+
33
+ # set to 'true' to allow .pyc and .pyo files without
34
+ # a source .py file to be detected as revisions in the
35
+ # versions/ directory
36
+ # sourceless = false
37
+
38
+ # version location specification; This defaults
39
+ # to migrations/versions. When using multiple version
40
+ # directories, initial revisions must be specified with --version-path.
41
+ # The path separator used here should be the separator specified by "version_path_separator" below.
42
+ # version_locations = %(here)s/bar:%(here)s/bat:migrations/versions
43
+
44
+ # version path separator; As mentioned above, this is the character used to split
45
+ # version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
46
+ # If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
47
+ # Valid values for version_path_separator are:
48
+ #
49
+ # version_path_separator = :
50
+ # version_path_separator = ;
51
+ # version_path_separator = space
52
+ # version_path_separator = newline
53
+ #
54
+ # Use os.pathsep. Default configuration used for new projects.
55
+ version_path_separator = os
56
+
57
+ # set to 'true' to search source files recursively
58
+ # in each "version_locations" directory
59
+ # new in Alembic version 1.10
60
+ # recursive_version_locations = false
61
+
62
+ # the output encoding used when revision files
63
+ # are written from script.py.mako
64
+ # output_encoding = utf-8
65
+
66
+ sqlalchemy.url = sqlite:///./app.db
67
+
68
+
69
+ [post_write_hooks]
70
+ # post_write_hooks defines scripts or Python functions that are run
71
+ # on newly generated revision scripts. See the documentation for further
72
+ # detail and examples
73
+
74
+ # format using "black" - use the console_scripts runner, against the "black" entrypoint
75
+ # hooks = black
76
+ # black.type = console_scripts
77
+ # black.entrypoint = black
78
+ # black.options = -l 79 REVISION_SCRIPT_FILENAME
79
+
80
+ # lint with attempts to fix using "ruff" - use the exec runner, execute a binary
81
+ # hooks = ruff
82
+ # ruff.type = exec
83
+ # ruff.executable = %(here)s/.venv/bin/ruff
84
+ # ruff.options = check --fix REVISION_SCRIPT_FILENAME
85
+
86
+ # Logging configuration
87
+ [loggers]
88
+ keys = root,sqlalchemy,alembic
89
+
90
+ [handlers]
91
+ keys = console
92
+
93
+ [formatters]
94
+ keys = generic
95
+
96
+ [logger_root]
97
+ level = WARNING
98
+ handlers = console
99
+ qualname =
100
+
101
+ [logger_sqlalchemy]
102
+ level = WARNING
103
+ handlers =
104
+ qualname = sqlalchemy.engine
105
+
106
+ [logger_alembic]
107
+ level = INFO
108
+ handlers =
109
+ qualname = alembic
110
+
111
+ [handler_console]
112
+ class = StreamHandler
113
+ args = (sys.stderr,)
114
+ level = NOTSET
115
+ formatter = generic
116
+
117
+ [formatter_generic]
118
+ format = %(levelname)-5.5s [%(name)s] %(message)s
119
+ datefmt = %H:%M:%S
app.db ADDED
Binary file (49.2 kB). View file
 
database.py CHANGED
@@ -1,19 +1,14 @@
1
  from sqlalchemy import create_engine
2
- from sqlalchemy.ext.declarative import declarative_base
3
  from sqlalchemy.orm import sessionmaker
4
 
5
- SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
6
-
7
- engine = create_engine(
8
- SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
9
- )
10
  SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
11
 
12
- Base = declarative_base()
13
 
14
  def get_db():
15
  db = SessionLocal()
16
  try:
17
  yield db
18
  finally:
19
- db.close()
 
1
  from sqlalchemy import create_engine
 
2
  from sqlalchemy.orm import sessionmaker
3
 
4
+ SQLALCHEMY_DATABASE_URL = "sqlite:///./app.db"
5
+ engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
 
 
 
6
  SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
7
 
 
8
 
9
  def get_db():
10
  db = SessionLocal()
11
  try:
12
  yield db
13
  finally:
14
+ db.close()
main.py CHANGED
@@ -1,14 +1,17 @@
1
  from fastapi import FastAPI
2
- from routes.extract_product_info_from_barcode import router as extract_product_info_router
3
- from routes.fetch_product_data import router as fetch_product_data_router
4
- from routes.auth import router as auth_router
5
- from routes.analysis import router as analysis_router
6
- from routes.history import router as history_router
7
 
8
  app = FastAPI()
 
9
 
10
- app.include_router(extract_product_info_router, prefix="/api")
11
- app.include_router(fetch_product_data_router, prefix="/api")
 
 
12
  app.include_router(auth_router, prefix="/api")
13
  app.include_router(analysis_router, prefix="/api")
14
  app.include_router(history_router, prefix="/api")
 
1
  from fastapi import FastAPI
2
+ from routers.auth import router as auth_router
3
+ from routers.analysis import router as analysis_router
4
+ from routers.history import router as history_router
5
+ from database import get_db,engine
6
+ from models.base import Base
7
 
8
  app = FastAPI()
9
+ db = get_db()
10
 
11
+ @app.on_event("startup")
12
+ async def create_tables():
13
+ Base.metadata.create_all(bind=engine)
14
+
15
  app.include_router(auth_router, prefix="/api")
16
  app.include_router(analysis_router, prefix="/api")
17
  app.include_router(history_router, prefix="/api")
migrations/README ADDED
@@ -0,0 +1 @@
 
 
1
+ Generic single-database configuration.
migrations/env.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from logging.config import fileConfig
2
+ from sqlalchemy import engine_from_config
3
+ from sqlalchemy import pool
4
+ from alembic import context
5
+
6
+ # Import your models and Base
7
+ from models.base import Base
8
+ from models.user import User
9
+ from models.user_preferences import UserPreferences
10
+ # Import other models as needed
11
+
12
+ # this is the Alembic Config object
13
+ config = context.config
14
+
15
+ # Interpret the config file for Python logging
16
+ if config.config_file_name is not None:
17
+ fileConfig(config.config_file_name)
18
+
19
+ # Set target metadata
20
+ target_metadata = Base.metadata
21
+
22
+ def run_migrations_offline() -> None:
23
+ url = config.get_main_option("sqlalchemy.url")
24
+ context.configure(
25
+ url=url,
26
+ target_metadata=target_metadata,
27
+ literal_binds=True,
28
+ dialect_opts={"paramstyle": "named"},
29
+ )
30
+
31
+ with context.begin_transaction():
32
+ context.run_migrations()
33
+
34
+ def run_migrations_online() -> None:
35
+ connectable = engine_from_config(
36
+ config.get_section(config.config_ini_section, {}),
37
+ prefix="sqlalchemy.",
38
+ poolclass=pool.NullPool,
39
+ )
40
+
41
+ with connectable.connect() as connection:
42
+ context.configure(
43
+ connection=connection,
44
+ target_metadata=target_metadata
45
+ )
46
+
47
+ with context.begin_transaction():
48
+ context.run_migrations()
49
+
50
+ if context.is_offline_mode():
51
+ run_migrations_offline()
52
+ else:
53
+ run_migrations_online()
migrations/script.py.mako ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """${message}
2
+
3
+ Revision ID: ${up_revision}
4
+ Revises: ${down_revision | comma,n}
5
+ Create Date: ${create_date}
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+ ${imports if imports else ""}
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = ${repr(up_revision)}
16
+ down_revision: Union[str, None] = ${repr(down_revision)}
17
+ branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
18
+ depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
19
+
20
+
21
+ def upgrade() -> None:
22
+ """Upgrade schema."""
23
+ ${upgrades if upgrades else "pass"}
24
+
25
+
26
+ def downgrade() -> None:
27
+ """Downgrade schema."""
28
+ ${downgrades if downgrades else "pass"}
migrations/versions/605bfa8313bb_create_initial_tables.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """create initial tables
2
+
3
+ Revision ID: 605bfa8313bb
4
+ Revises:
5
+ Create Date: 2025-03-21 14:59:03.528627
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = '605bfa8313bb'
16
+ down_revision: Union[str, None] = None
17
+ branch_labels: Union[str, Sequence[str], None] = None
18
+ depends_on: Union[str, Sequence[str], None] = None
19
+
20
+
21
+ def upgrade() -> None:
22
+ """Upgrade schema."""
23
+ # ### commands auto generated by Alembic - please adjust! ###
24
+ op.create_table('ingredients',
25
+ sa.Column('id', sa.Integer(), nullable=False),
26
+ sa.Column('name', sa.String(), nullable=True),
27
+ sa.Column('nutritional_info', sa.JSON(), nullable=True),
28
+ sa.PrimaryKeyConstraint('id')
29
+ )
30
+ op.create_index(op.f('ix_ingredients_id'), 'ingredients', ['id'], unique=False)
31
+ op.create_index(op.f('ix_ingredients_name'), 'ingredients', ['name'], unique=True)
32
+ op.create_table('scan_history',
33
+ sa.Column('id', sa.Integer(), nullable=False),
34
+ sa.Column('user_id', sa.Integer(), nullable=False),
35
+ sa.Column('product_id', sa.Integer(), nullable=False),
36
+ sa.Column('scan_date', sa.DateTime(), nullable=False),
37
+ sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
38
+ sa.PrimaryKeyConstraint('id')
39
+ )
40
+ op.create_index(op.f('ix_scan_history_id'), 'scan_history', ['id'], unique=False)
41
+ # ### end Alembic commands ###
42
+
43
+
44
+ def downgrade() -> None:
45
+ """Downgrade schema."""
46
+ # ### commands auto generated by Alembic - please adjust! ###
47
+ op.drop_index(op.f('ix_scan_history_id'), table_name='scan_history')
48
+ op.drop_table('scan_history')
49
+ op.drop_index(op.f('ix_ingredients_name'), table_name='ingredients')
50
+ op.drop_index(op.f('ix_ingredients_id'), table_name='ingredients')
51
+ op.drop_table('ingredients')
52
+ # ### end Alembic commands ###
migrations/versions/fcf5a0dab27d_add_scan_history_relationship.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """add scan history relationship
2
+
3
+ Revision ID: fcf5a0dab27d
4
+ Revises: 605bfa8313bb
5
+ Create Date: 2025-03-21 17:33:44.076205
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = 'fcf5a0dab27d'
16
+ down_revision: Union[str, None] = '605bfa8313bb'
17
+ branch_labels: Union[str, Sequence[str], None] = None
18
+ depends_on: Union[str, Sequence[str], None] = None
19
+
20
+
21
+ def upgrade() -> None:
22
+ """Upgrade schema."""
23
+ # ### commands auto generated by Alembic - please adjust! ###
24
+ op.drop_index('ix_scan_history_id', table_name='scan_history')
25
+ op.drop_constraint(None, 'scan_history', type_='foreignkey')
26
+ op.create_foreign_key(None, 'scan_history', 'users', ['user_id'], ['id'], ondelete='CASCADE')
27
+ # ### end Alembic commands ###
28
+
29
+
30
+ def downgrade() -> None:
31
+ """Downgrade schema."""
32
+ # ### commands auto generated by Alembic - please adjust! ###
33
+ op.drop_constraint(None, 'scan_history', type_='foreignkey')
34
+ op.create_foreign_key(None, 'scan_history', 'users', ['user_id'], ['id'])
35
+ op.create_index('ix_scan_history_id', 'scan_history', ['id'], unique=False)
36
+ # ### end Alembic commands ###
models/__init__.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ from .base import Base
2
+ from .user import User
3
+ from .user_preferences import UserPreferences
4
+ from .ingredient import Ingredient
5
+ from .scan_history import ScanHistory
6
+
7
+ __all__ = ["Base", "User", "UserPreferences", "Ingredient", "ScanHistory"]
models/base.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from sqlalchemy.orm import DeclarativeBase
2
+
3
+ class Base(DeclarativeBase):
4
+ pass
models/ingredient.py CHANGED
@@ -1,5 +1,5 @@
1
  from sqlalchemy import Column, Integer, String, JSON
2
- from database import Base
3
 
4
  class Ingredient(Base):
5
  __tablename__ = "ingredients"
 
1
  from sqlalchemy import Column, Integer, String, JSON
2
+ from .base import Base
3
 
4
  class Ingredient(Base):
5
  __tablename__ = "ingredients"
models/scan_history.py CHANGED
@@ -1,13 +1,16 @@
1
- from sqlalchemy import Column, Integer, ForeignKey, DateTime
2
- from sqlalchemy.orm import relationship
3
- from database import Base
 
 
4
 
5
  class ScanHistory(Base):
6
  __tablename__ = "scan_history"
7
 
8
- id = Column(Integer, primary_key=True, index=True)
9
- user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
10
- product_id = Column(Integer, nullable=False)
11
- scan_date = Column(DateTime, nullable=False)
12
 
13
- user = relationship("User", back_populates="scan_history")
 
 
1
+ from sqlalchemy.orm import Mapped, mapped_column, relationship
2
+ from sqlalchemy import ForeignKey, Integer, DateTime
3
+ from datetime import datetime
4
+ from .base import Base
5
+ from typing import Optional
6
 
7
  class ScanHistory(Base):
8
  __tablename__ = "scan_history"
9
 
10
+ id: Mapped[int] = mapped_column(primary_key=True)
11
+ user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"))
12
+ product_id: Mapped[int] = mapped_column(Integer)
13
+ scan_date: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
14
 
15
+ # Add relationship to user
16
+ user: Mapped["User"] = relationship("User", back_populates="scan_history")
models/user.py CHANGED
@@ -1,10 +1,28 @@
1
- from sqlalchemy import Column, Integer, String
2
- from database import Base
 
 
 
 
 
3
 
4
  class User(Base):
5
  __tablename__ = "users"
6
 
7
- id = Column(Integer, primary_key=True, index=True)
8
- username = Column(String, unique=True, index=True)
9
- email = Column(String, unique=True, index=True)
10
- hashed_password = Column(String)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy.orm import Mapped, mapped_column, relationship
2
+ from sqlalchemy import String, Boolean
3
+ from typing import List, TYPE_CHECKING
4
+ from .base import Base
5
+
6
+ if TYPE_CHECKING:
7
+ from .user_preferences import UserPreferences
8
 
9
  class User(Base):
10
  __tablename__ = "users"
11
 
12
+ id: Mapped[int] = mapped_column(primary_key=True)
13
+ username: Mapped[str] = mapped_column(String, unique=True, index=True)
14
+ email: Mapped[str] = mapped_column(String, unique=True, index=True)
15
+ hashed_password: Mapped[str] = mapped_column(String)
16
+ is_active: Mapped[bool] = mapped_column(Boolean, default=True)
17
+
18
+ preferences: Mapped[List["UserPreferences"]] = relationship(
19
+ "UserPreferences",
20
+ back_populates="user",
21
+ cascade="all, delete-orphan"
22
+ )
23
+ # Add relationship to scan history
24
+ scan_history: Mapped[List["ScanHistory"]] = relationship(
25
+ "ScanHistory",
26
+ back_populates="user",
27
+ cascade="all, delete-orphan"
28
+ )
models/user_preferences.py CHANGED
@@ -1,15 +1,19 @@
1
- from sqlalchemy import Column, Integer, String, ForeignKey
2
- from sqlalchemy.orm import relationship
3
- from database import Base
 
 
 
 
4
 
5
  class UserPreferences(Base):
6
  __tablename__ = "user_preferences"
7
 
8
- id = Column(Integer, primary_key=True, index=True)
9
- user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
10
- dietary_restrictions = Column(String, nullable=True)
11
- allergens = Column(String, nullable=True)
12
- preferred_ingredients = Column(String, nullable=True)
13
- disliked_ingredients = Column(String, nullable=True)
14
 
15
- user = relationship("User", back_populates="preferences")
 
1
+ from sqlalchemy.orm import Mapped, mapped_column, relationship
2
+ from sqlalchemy import ForeignKey, String
3
+ from typing import Optional, TYPE_CHECKING
4
+ from .base import Base
5
+
6
+ if TYPE_CHECKING:
7
+ from .user import User
8
 
9
  class UserPreferences(Base):
10
  __tablename__ = "user_preferences"
11
 
12
+ id: Mapped[int] = mapped_column(primary_key=True)
13
+ user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"))
14
+ dietary_restrictions: Mapped[Optional[str]] = mapped_column(String, nullable=True)
15
+ allergens: Mapped[Optional[str]] = mapped_column(String, nullable=True)
16
+ preferred_ingredients: Mapped[Optional[str]] = mapped_column(String, nullable=True)
17
+ disliked_ingredients: Mapped[Optional[str]] = mapped_column(String, nullable=True)
18
 
19
+ user: Mapped["User"] = relationship("User", back_populates="preferences")
requirements.txt CHANGED
@@ -8,3 +8,11 @@ sqlalchemy
8
  alembic
9
  redis
10
  transformers
 
 
 
 
 
 
 
 
 
8
  alembic
9
  redis
10
  transformers
11
+ passlib
12
+ python-jose
13
+ python-multipart
14
+ cachetools
15
+ pytest
16
+ pytest-cov
17
+ httpx
18
+ bcrypt
routers/__init__.py ADDED
File without changes
routes/extract_product_info_from_barcode.py DELETED
@@ -1,45 +0,0 @@
1
- from fastapi import APIRouter, HTTPException
2
- from utils import fetch_product_data_from_api
3
- from models import ProductInfo
4
-
5
- router = APIRouter()
6
-
7
- @router.get("/extract_product_info", response_model=ProductInfo)
8
- def extract_product_info(barcode: str):
9
- data = fetch_product_data_from_api(barcode)
10
- product = data.get('product', {})
11
-
12
- product_info = {
13
- "product_name": product.get('product_name_en', product.get('product_name', 'N/A')),
14
- "generic_name": product.get('generic_name_en', product.get('generic_name', 'N/A')),
15
- "brands": product.get('brands', 'N/A'),
16
- "ingredients": [],
17
- "ingredients_text": product.get('ingredients_text_en', product.get('ingredients_text', 'N/A')),
18
- "ingredients_analysis": product.get('ingredients_analysis', {}),
19
- "nutriscore": product.get('nutriscore', {}),
20
- "nutrient_levels": product.get('nutrient_levels', {}),
21
- "nutriments": product.get('nutriments', {}),
22
- "data_quality_warnings": product.get('data_quality_warnings_tags', [])
23
- }
24
-
25
- ingredients_list = product.get('ingredients', [])
26
- for ingredient in ingredients_list:
27
- ingredient_info = {
28
- "text": ingredient.get('text', 'N/A'),
29
- "percent": ingredient.get('percent', ingredient.get('percent_estimate', 'N/A')),
30
- "vegan": ingredient.get('vegan', 'N/A'),
31
- "vegetarian": ingredient.get('vegetarian', 'N/A'),
32
- "sub_ingredients": []
33
- }
34
- sub_ingredients = ingredient.get('ingredients', [])
35
- for sub_ingredient in sub_ingredients:
36
- sub_ingredient_info = {
37
- "text": sub_ingredient.get('text', 'N/A'),
38
- "percent": sub_ingredient.get('percent', sub_ingredient.get('percent_estimate', 'N/A')),
39
- "vegan": sub_ingredient.get('vegan', 'N/A'),
40
- "vegetarian": sub_ingredient.get('vegetarian', 'N/A')
41
- }
42
- ingredient_info["sub_ingredients"].append(sub_ingredient_info)
43
- product_info["ingredients"].append(ingredient_info)
44
-
45
- return product_info
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
routes/fetch_product_data.py DELETED
@@ -1,12 +0,0 @@
1
- from fastapi import APIRouter
2
- from models import Barcodes
3
- from utils import fetch_product_data_from_api, save_json_file
4
-
5
- router = APIRouter()
6
-
7
- @router.post("/fetch_product_data")
8
- def fetch_product_data(barcodes: Barcodes):
9
- for item, barcode in barcodes.barcodes.items():
10
- data = fetch_product_data_from_api(barcode)
11
- save_json_file(item, data)
12
- return {"message": "Data fetched and saved successfully"}
 
 
 
 
 
 
 
 
 
 
 
 
 
services/__init__.py ADDED
File without changes
services/auth_service.py CHANGED
@@ -3,7 +3,7 @@ from jose import JWTError, jwt
3
  from datetime import datetime, timedelta
4
  from fastapi import Depends, HTTPException, status
5
  from fastapi.security import OAuth2PasswordBearer
6
- from sqlalchemy.orm import Session
7
  from database import get_db
8
  from models.user import User
9
  from pydantic import BaseModel
@@ -24,8 +24,6 @@ class Token(BaseModel):
24
  class TokenData(BaseModel):
25
  username: str | None = None
26
 
27
- class UserInDB(User):
28
- hashed_password: str
29
 
30
  def verify_password(plain_password, hashed_password):
31
  return pwd_context.verify(plain_password, hashed_password)
 
3
  from datetime import datetime, timedelta
4
  from fastapi import Depends, HTTPException, status
5
  from fastapi.security import OAuth2PasswordBearer
6
+ from sqlalchemy.orm import Session,Mapped
7
  from database import get_db
8
  from models.user import User
9
  from pydantic import BaseModel
 
24
  class TokenData(BaseModel):
25
  username: str | None = None
26
 
 
 
27
 
28
  def verify_password(plain_password, hashed_password):
29
  return pwd_context.verify(plain_password, hashed_password)
services/scan_history.py CHANGED
@@ -2,12 +2,19 @@ from sqlalchemy.orm import Session
2
  from models.scan_history import ScanHistory
3
  from datetime import datetime
4
 
5
- def record_scan(db: Session, user_id: int, product_id: int):
6
- scan_entry = ScanHistory(user_id=user_id, product_id=product_id, scan_date=datetime.utcnow())
 
 
 
 
7
  db.add(scan_entry)
8
  db.commit()
9
  db.refresh(scan_entry)
10
  return scan_entry
11
 
12
- def get_scan_history(db: Session, user_id: int):
13
- return db.query(ScanHistory).filter(ScanHistory.user_id == user_id).all()
 
 
 
 
2
  from models.scan_history import ScanHistory
3
  from datetime import datetime
4
 
5
+ def record_scan(db: Session, user_id: int, product_id: int) -> ScanHistory:
6
+ scan_entry = ScanHistory(
7
+ user_id=user_id,
8
+ product_id=product_id,
9
+ scan_date=datetime.utcnow()
10
+ )
11
  db.add(scan_entry)
12
  db.commit()
13
  db.refresh(scan_entry)
14
  return scan_entry
15
 
16
+ def get_scan_history(db: Session, user_id: int) -> list[ScanHistory]:
17
+ return db.query(ScanHistory)\
18
+ .filter(ScanHistory.user_id == user_id)\
19
+ .order_by(ScanHistory.scan_date.desc())\
20
+ .all()
test.db ADDED
Binary file (36.9 kB). View file
 
tests/__init__.py ADDED
File without changes
tests/test_auth_service.py CHANGED
@@ -2,7 +2,8 @@ import pytest
2
  from fastapi.testclient import TestClient
3
  from sqlalchemy import create_engine
4
  from sqlalchemy.orm import sessionmaker
5
- from database import Base, get_db
 
6
  from main import app
7
  from services.auth_service import create_user, get_password_hash
8
  from models.user import User
 
2
  from fastapi.testclient import TestClient
3
  from sqlalchemy import create_engine
4
  from sqlalchemy.orm import sessionmaker
5
+ from database import get_db
6
+ from models.base import Base
7
  from main import app
8
  from services.auth_service import create_user, get_password_hash
9
  from models.user import User