Skip to content

Commit d151392

Browse files
authored
Add print fact and file number of pages (#45)
https://t.me/c/1758480664/28190/48403
1 parent d1a3c90 commit d151392

File tree

9 files changed

+113
-23
lines changed

9 files changed

+113
-23
lines changed

migrations/versions/44494b133481_print_options.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,12 @@
1717

1818

1919
def upgrade():
20-
# ### commands auto generated by Alembic - please adjust! ###
2120
op.add_column('file', sa.Column('option_pages', sa.String(), nullable=True))
2221
op.add_column('file', sa.Column('option_copies', sa.Integer(), nullable=True))
2322
op.add_column('file', sa.Column('option_two_sided', sa.Boolean(), nullable=True))
24-
# ### end Alembic commands ###
2523

2624

2725
def downgrade():
28-
# ### commands auto generated by Alembic - please adjust! ###
2926
op.drop_column('file', 'option_two_sided')
3027
op.drop_column('file', 'option_copies')
3128
op.drop_column('file', 'option_pages')
32-
# ### end Alembic commands ###
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""add_file_number_of_pages
2+
3+
Revision ID: 686a37a323be
4+
Revises: 692fe4f50da7
5+
Create Date: 2023-04-29 19:38:02.676614
6+
7+
"""
8+
import sqlalchemy as sa
9+
from alembic import op
10+
11+
12+
# revision identifiers, used by Alembic.
13+
revision = '686a37a323be'
14+
down_revision = '692fe4f50da7'
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
op.add_column('file', sa.Column('number_of_pages', sa.Integer(), nullable=True))
21+
22+
23+
def downgrade():
24+
op.drop_column('file', 'number_of_pages')

migrations/versions/692fe4f50da7_upper_surnames.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,4 @@ def upgrade():
2121

2222

2323
def downgrade():
24-
# ### commands auto generated by Alembic - please adjust! ###
2524
pass
26-
# ### end Alembic commands ###
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""add_print_fact
2+
3+
Revision ID: f6fb6304fb74
4+
Revises: 686a37a323be
5+
Create Date: 2023-04-29 21:36:34.034457
6+
7+
"""
8+
import sqlalchemy as sa
9+
from alembic import op
10+
11+
12+
# revision identifiers, used by Alembic.
13+
revision = 'f6fb6304fb74'
14+
down_revision = '686a37a323be'
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
op.create_table(
21+
'print_fact',
22+
sa.Column('id', sa.Integer(), nullable=False),
23+
sa.Column('file_id', sa.Integer(), nullable=False),
24+
sa.Column('owner_id', sa.Integer(), nullable=False),
25+
sa.Column('created_at', sa.DateTime(), nullable=False),
26+
sa.ForeignKeyConstraint(
27+
['file_id'],
28+
['file.id'],
29+
),
30+
sa.ForeignKeyConstraint(
31+
['owner_id'],
32+
['union_member.id'],
33+
),
34+
sa.PrimaryKeyConstraint('id'),
35+
)
36+
37+
38+
def downgrade():
39+
op.drop_table('print_fact')

print_service/models/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class UnionMember(Model):
2323
student_number: Mapped[str] = mapped_column(String, nullable=True)
2424

2525
files: Mapped[list[File]] = relationship('File', back_populates='owner')
26+
print_facts: Mapped[list[PrintFact]] = relationship('PrintFact', back_populates='owner')
2627

2728

2829
class File(Model):
@@ -39,5 +40,19 @@ class File(Model):
3940
updated_at: Mapped[datetime] = Column(
4041
DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow
4142
)
43+
number_of_pages: Mapped[int] = Column(Integer)
4244

4345
owner: Mapped[UnionMember] = relationship('UnionMember', back_populates='files')
46+
print_facts: Mapped[list[PrintFact]] = relationship('PrintFact', back_populates='file')
47+
48+
49+
class PrintFact(Model):
50+
__tablename__ = 'print_fact'
51+
52+
id: Mapped[int] = Column(Integer, primary_key=True)
53+
file_id: Mapped[int] = Column(Integer, ForeignKey('file.id'), nullable=False)
54+
owner_id: Mapped[int] = Column(Integer, ForeignKey('union_member.id'), nullable=False)
55+
created_at: Mapped[datetime] = Column(DateTime, nullable=False, default=datetime.utcnow)
56+
57+
owner: Mapped[UnionMember] = relationship('UnionMember', back_populates='print_facts')
58+
file: Mapped[File] = relationship('File', back_populates='print_facts')

print_service/routes/file.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from print_service.models import UnionMember
1616
from print_service.schema import BaseModel
1717
from print_service.settings import Settings, get_settings
18-
from print_service.utils import check_pdf_ok, generate_filename, generate_pin, get_file
18+
from print_service.utils import checking_for_pdf, generate_filename, generate_pin, get_file
1919

2020

2121
logger = logging.getLogger(__name__)
@@ -173,7 +173,10 @@ async def upload_file(
173173
if len(memory_file) > settings.MAX_SIZE:
174174
raise HTTPException(413, f'Content too large, {settings.MAX_SIZE} bytes allowed')
175175
await saved_file.write(memory_file)
176-
if not check_pdf_ok(memory_file):
176+
pdf_ok, number_of_pages = checking_for_pdf(memory_file)
177+
file_model.number_of_pages = number_of_pages
178+
db.session.commit()
179+
if not pdf_ok:
177180
await aiofiles.os.remove(path)
178181
raise HTTPException(415, 'File corrupted')
179182
await file.close()

print_service/utils/__init__.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
from print_service.models import File
1414
from print_service.models import File as FileModel
15+
from print_service.models import PrintFact
1516
from print_service.settings import Settings, get_settings
1617

1718

@@ -72,12 +73,24 @@ def get_file(dbsession, pin: str or list[str]):
7273
},
7374
}
7475
)
76+
file_model = PrintFact(file_id=f.id, owner_id=f.owner_id)
77+
dbsession.add(file_model)
78+
dbsession.commit()
7579
return result
7680

7781

78-
def check_pdf_ok(f: bytes):
82+
def checking_for_pdf(f: bytes) -> tuple[bool, int]:
83+
"""_summary_
84+
85+
Args:
86+
f (bytes): file to check
87+
88+
Returns:
89+
tuple[bool, int]: The first argument returns whether the file is a valid pdf.
90+
The second argument returns the number of pages in the pdf document (0- if the check failed)
91+
"""
7992
try:
80-
PdfFileReader(io.BytesIO(f))
81-
return True
93+
pdf_file = PdfFileReader(io.BytesIO(f))
94+
return True, pdf_file.getNumPages()
8295
except Exception:
83-
return False
96+
return False, 0

tests/test_routes/conftest.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import os
2-
from unittest.mock import Mock
32

43
import pytest
54

6-
from print_service.models import File, UnionMember
5+
from print_service.models import File, PrintFact, UnionMember
76

87

98
@pytest.fixture(scope='function')
@@ -19,6 +18,7 @@ def union_member_user(dbsession):
1918
yield union_member
2019
db_user = dbsession.query(UnionMember).filter(UnionMember.id == union_member['id']).one_or_none()
2120
assert db_user is not None
21+
dbsession.query(PrintFact).filter(PrintFact.owner_id == union_member['id']).delete()
2222
dbsession.query(UnionMember).filter(UnionMember.id == union_member['id']).delete()
2323
dbsession.commit()
2424

@@ -34,6 +34,9 @@ def uploaded_file_db(dbsession, union_member_user, client):
3434
res = client.post('/file', json=body)
3535
db_file = dbsession.query(File).filter(File.pin == res.json()['pin']).one_or_none()
3636
yield db_file
37+
file = dbsession.query(File).filter(File.pin == res.json()['pin']).one_or_none()
38+
assert file is not None
39+
dbsession.query(PrintFact).filter(PrintFact.file_id == file.id).delete()
3740
dbsession.query(File).filter(File.pin == res.json()['pin']).delete()
3841
dbsession.commit()
3942

@@ -57,5 +60,8 @@ def pin_pdf(dbsession, union_member_user, client):
5760
res = client.post('/file', json=body)
5861
pin = res.json()['pin']
5962
yield pin
63+
file = dbsession.query(File).filter(File.pin == res.json()['pin']).one_or_none()
64+
assert file is not None
65+
dbsession.query(PrintFact).filter(PrintFact.file_id == file.id).delete()
6066
dbsession.query(File).filter(File.pin == res.json()['pin']).delete()
6167
dbsession.commit()

tests/test_routes/test_file.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
1-
import asyncio
2-
import datetime
31
import json
4-
import time
5-
from concurrent.futures import ThreadPoolExecutor
62

73
import pytest
84
from fastapi import HTTPException
95
from starlette import status
106

117
from print_service.models import File
128
from print_service.settings import get_settings
13-
from print_service.utils import check_pdf_ok, generate_filename, get_file
9+
from print_service.utils import checking_for_pdf, get_file
1410

1511

1612
url = '/file'
@@ -91,8 +87,8 @@ def test_get_file_func_2_not_exists(dbsession, uploaded_file_os):
9187

9288

9389
def test_file_check():
94-
assert check_pdf_ok(open("tests/test_routes/test_files/broken.pdf", "rb").read()) is False
95-
assert check_pdf_ok(open("tests/test_routes/test_files/correct.pdf", "rb").read()) is True
90+
assert checking_for_pdf(open("tests/test_routes/test_files/broken.pdf", "rb").read()) == (False, 0)
91+
assert checking_for_pdf(open("tests/test_routes/test_files/correct.pdf", "rb").read()) == (True, 2)
9692

9793

9894
def test_upload_and_print_correct_pdf(pin_pdf, client):
@@ -130,9 +126,9 @@ def test_upload_and_print_encrypted_file(pin_pdf, client):
130126
fileName = 'tests/test_routes/test_files/encrypted.pdf'
131127
files = {'file': (f"{fileName}", open(f"{fileName}", 'rb'), "application/pdf")}
132128
res = client.post(f"{url}/{pin}", files=files)
133-
assert res.status_code == status.HTTP_200_OK
129+
assert res.status_code == status.HTTP_415_UNSUPPORTED_MEDIA_TYPE
134130
res2 = client.get(f"{url}/{pin}")
135-
assert res2.status_code == status.HTTP_200_OK
131+
assert res2.status_code == status.HTTP_415_UNSUPPORTED_MEDIA_TYPE
136132

137133

138134
def test_incorrect_filename(union_member_user, client, dbsession):

0 commit comments

Comments
 (0)