From 0a4c54af08551889d0efabd8aecb8d6d5187a830 Mon Sep 17 00:00:00 2001 From: AdailtonHolanda Date: Mon, 30 Jun 2025 09:22:19 -0300 Subject: [PATCH] Devtest solution. --- app/__init__.py | 0 app/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 181 bytes app/__pycache__/main.cpython-311.pyc | Bin 0 -> 1455 bytes app/database/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 190 bytes .../__pycache__/elevators.cpython-311.pyc | Bin 0 -> 821 bytes app/database/elevators.py | 14 +++ app/main.py | 26 ++++++ app/models/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 188 bytes .../__pycache__/elevators.cpython-311.pyc | Bin 0 -> 1461 bytes app/models/elevators.py | 18 ++++ app/routers/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 189 bytes .../__pycache__/elevators.cpython-311.pyc | Bin 0 -> 4756 bytes app/routers/elevators.py | 88 ++++++++++++++++++ app/schemas/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 189 bytes .../__pycache__/elevators.cpython-311.pyc | Bin 0 -> 1574 bytes app/schemas/elevators.py | 27 ++++++ chatgpt/app_tests.py | 10 -- chatgpt/db.sql | 12 --- chatgpt/main.py | 43 --------- chatgpt/requirements.txt | 4 - elevators.db | Bin 0 -> 24576 bytes explanation.md | 53 +++++++++++ readme.md | 21 +++++ requirements.txt | 7 ++ tests/__init__.py | 0 tests/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 183 bytes .../endpoints.cpython-311-pytest-7.4.0.pyc | Bin 0 -> 9959 bytes ...est_endpoints.cpython-311-pytest-7.4.0.pyc | Bin 0 -> 9964 bytes ...est_endpoints.cpython-311-pytest-8.4.1.pyc | Bin 0 -> 9885 bytes tests/test_endpoints.py | 40 ++++++++ 34 files changed, 294 insertions(+), 69 deletions(-) create mode 100644 app/__init__.py create mode 100644 app/__pycache__/__init__.cpython-311.pyc create mode 100644 app/__pycache__/main.cpython-311.pyc create mode 100644 app/database/__init__.py create mode 100644 app/database/__pycache__/__init__.cpython-311.pyc create mode 100644 app/database/__pycache__/elevators.cpython-311.pyc create mode 100644 app/database/elevators.py create mode 100644 app/main.py create mode 100644 app/models/__init__.py create mode 100644 app/models/__pycache__/__init__.cpython-311.pyc create mode 100644 app/models/__pycache__/elevators.cpython-311.pyc create mode 100644 app/models/elevators.py create mode 100644 app/routers/__init__.py create mode 100644 app/routers/__pycache__/__init__.cpython-311.pyc create mode 100644 app/routers/__pycache__/elevators.cpython-311.pyc create mode 100644 app/routers/elevators.py create mode 100644 app/schemas/__init__.py create mode 100644 app/schemas/__pycache__/__init__.cpython-311.pyc create mode 100644 app/schemas/__pycache__/elevators.cpython-311.pyc create mode 100644 app/schemas/elevators.py delete mode 100644 chatgpt/app_tests.py delete mode 100644 chatgpt/db.sql delete mode 100644 chatgpt/main.py delete mode 100644 chatgpt/requirements.txt create mode 100644 elevators.db create mode 100644 explanation.md create mode 100644 requirements.txt create mode 100644 tests/__init__.py create mode 100644 tests/__pycache__/__init__.cpython-311.pyc create mode 100644 tests/__pycache__/endpoints.cpython-311-pytest-7.4.0.pyc create mode 100644 tests/__pycache__/test_endpoints.cpython-311-pytest-7.4.0.pyc create mode 100644 tests/__pycache__/test_endpoints.cpython-311-pytest-8.4.1.pyc create mode 100644 tests/test_endpoints.py diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/__pycache__/__init__.cpython-311.pyc b/app/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f04567ee665119974c31bc8c695b238d56748da7 GIT binary patch literal 181 zcmZ3^%ge<81ium!GC=fW5CH>>P{wCAAY(d13PUi1CZpdSBH%@qw9s literal 0 HcmV?d00001 diff --git a/app/__pycache__/main.cpython-311.pyc b/app/__pycache__/main.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd969039c338a8f3c018d4c572a3d18cc650ab46 GIT binary patch literal 1455 zcmZuv&1)M+6rWvi1p61v}Sf!%#PeR zmny-K91A@phaP-sN{Ro69{LZgf*@j`VCbd2xtLsf%A1uow%op*ee-7C$GqQr@0V(| zOpt#2eyexSCge|Z$S5}xPMAf=6T%6%1ZkVy7FJ-lZOh0xkqc;>rZN@zz-c={p`WHeA9QOXP01)(qAk-&XV`BB*UGPBAnUkT8KSh-RS z`d*fZ*-ekkf3lD42~o&1+IdsI1MKX7Njax?ht_#as(7kB$e+rm61UI$@Eq2G-v6=8 zkk$ubii5#D@DL?uQRo5tt_LAgJz%pUF}L4mDYAGq_1jPk!gwGdMl)A2R|v*k*TBc(sPC)r$gXp_eaI6t;eU zfFUtK_N~N}MK5Z3zLLJziF*M37KdF0v1%deR^a;KYJX3cvDvfU%1an=5&!r@WJB`m z0?C*5zj{gQFKGSe8wstCXmd=P32pvC%a3k7yp=3oAJH3Qin`g7W@?+ux?nQ)!>%rD z<2<^3Uz$PD&U6|jHHWt`kJYTYtAIo3sz}CZHKw>P8H?$d#%Bx)#=Bw=bf!vO!3O!> z7g4zFcQxJET>n~^x}a`P%U0}kLN|a;M;dfGhf#GAapTm7Dn+dq{Lm8v4zqx=hK4B? zxs1}k#ZR%f0|B4O5AbMaJN^sVan7==30WAX--NuE%&!S~Kbc=gq?P>Nnvjo@`E^7p z!^(r|esx$qB1^-#NdtvR>B{4G#-%GyKOU8?jZ4>t&XH3cc9Z2s z;;f9Em9euj%pE(#&OfR@tUvZg*5$Ev`Pg=>#xX(mPmb8{zPYIpi@qea7o_&XCnIuo LOs*!`N-OaiGOuaR literal 0 HcmV?d00001 diff --git a/app/database/__init__.py b/app/database/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/database/__pycache__/__init__.cpython-311.pyc b/app/database/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5ce7911c8bb17a414b460970282005e614e4a535 GIT binary patch literal 190 zcmZ3^%ge<81XGd{GC=fW5CH>>P{wCAAY(d13PUi1CZpdlIY~;;_lhPbtkwwJTx;8V+(pF+Y&_z|6?V_<;dN6fpzE E09ywyng9R* literal 0 HcmV?d00001 diff --git a/app/database/__pycache__/elevators.cpython-311.pyc b/app/database/__pycache__/elevators.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a43753b229f96d4b5022697847522f7430a67337 GIT binary patch literal 821 zcmZuvy>1gh5Z=8zJGOIWemAxJnh*R0mJqxh8jadyuZiI6Z7 zMcyDKse%xB0Q$UuktnRIB2m#fa%iZS^*Q`zZui@7c6MiGXYPHuTp}o^uRGnRl#s7l zxs=?TO!tu-5`CAWFo?GO2^D^SXhqmb`_ zOe^c)1zhsPAHW({uzy4C27Utf9LH%m5Ws*Zu~ZGwNo!sgyd9=^2rTKMSMeDA+g=<+ z;3bU9ZH4ZwjvtA%3NL%U1Qtiki^I@Qym@-F1&cP5=@Kr71c*xoaScH+arB_`*sPmr zfw4#jV`+)8Fc$qlbDOc2Qt;3zCS~7x^V~&7u*k$IC4(kV2UdOR<I8P$a#WC6#Vht-kw}UpED>UQ5A&YuWxJ>hU>C=d1A9(OiNfe z9$<~aJ*m5qF8$tU#4?nYKCSV)s2U&C-G>cM0 z_M>n=tHJ|$9aUXL^$uZTQcB0<_UP|1CU-|yjk8L9XrC3A4{nXE>iF8~QGZmsGs&4H xL;coX6BUS=jk7AEJ{sONJ&))DN0OA%*n{tPtGhU%1n+g&PYuy&`(J%D@iRb(N8QW z(9g|JNzEzNkB`sH%PfhH*DI*}#bE;!EX_%^D`Ev24RS#-Kalvq%*e?2fdNJoF$2W_ D(U~s3 literal 0 HcmV?d00001 diff --git a/app/models/__pycache__/elevators.cpython-311.pyc b/app/models/__pycache__/elevators.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..76976e054944ef29a3d642da7eb27ca74d22b409 GIT binary patch literal 1461 zcmb7EyKfUQ824T7ndWGKQr=JrAr$qIt*8hwl%}XdyEHJ|a&lvr1bla??^4={ATeaj z#@eY+wF7?w5(8@@Wr#c>F|ic^3lpC&Nug;agdabD{(hfhe~-&s$4Mh-pI)w&w^W3_ z$jzYXec^im!W%>oQ5f=(ixerVOz~A$^;2%j*IdolT|LpKnBkkQsUQ_SLPUFwh)&Jj z6hiwjd(E{H&H&Cl!P&%GMLGMcG|ef(A~>dT#iuz<7!N~M^8;Zn1u=a}xv&<1ECMOa z`7mS@2XI20#}O^{uS%99M;L71Ac|atAXg=dn^F(sR=ZUoE`5~z>DQ7%yqlQ_ZaXc@UM|)eY!IQXjTc{Nx z^mP`7oEuUplOT**TnfS$!j^%JV(eG<(03rv&t-soIrM2B6I@|&800^`;uKF1I#J|! z4YP8XFI8e*DS1(u(rTX2%@}?okE_)@JdUy`PZ{0Bamb^Y>Nb}lN*uJ5wEJs=g?D?-lX^E4g&9K-19%!>m-M{`OzqC5|Odj6=1}z&`^N zWJG5M;fuH>y+|r9@#naYzpVScujc{31$GL|XdmD(r6@`lU2h#9T{PGF*XW`fcR j)0KON=p;0X-=y}(A1bRW)6#MnnhVl$T3QZMNvM7UL6l;g literal 0 HcmV?d00001 diff --git a/app/models/elevators.py b/app/models/elevators.py new file mode 100644 index 0000000..da92bef --- /dev/null +++ b/app/models/elevators.py @@ -0,0 +1,18 @@ +from datetime import datetime +from sqlalchemy import Column, Integer, DateTime, Boolean +from app.database.elevators import Base + +class Demand(Base): + __tablename__ = "demands" + + id = Column(Integer, primary_key=True, index=True) + floor = Column(Integer, nullable=False) + timestamp = Column(DateTime, default=datetime.utcnow) # ← OK aqui + + +class ElevatorState(Base): + __tablename__ = "elevator_states" + id = Column(Integer, primary_key=True, index=True) + timestamp = Column(DateTime, default=datetime.utcnow, index=True) + floor = Column(Integer, nullable=False) + vacant = Column(Boolean, nullable=False) \ No newline at end of file diff --git a/app/routers/__init__.py b/app/routers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/routers/__pycache__/__init__.cpython-311.pyc b/app/routers/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c3ebeb64b8807355aca9412fbeb792b50b2f2f69 GIT binary patch literal 189 zcmZ3^%ge<81Vu>+86f&Gh=2h`DC095kTIPhg&~+hlhJP_LlF~@{~09t)fY(UXXNLm z>L;coX6BUS=jk7AEJ{sONJ&))DN0OA%*n{tPtGhU%1n+g&PYuy&`(J%D@iRb(N8QW z&@aj_ElDjZ){l?R%*!l^kJl@x{Ka9Do1apelWJGQ3N#z!gkpXm@qw9ToXG}n^ZRFrKx~Tqsf!RmJ-z? zmDGZaDn_u%hJmz2gL*)JaDlG#mi&+cMVB8}>>rQ<0TBoU$vO;Je$x;X2!{Qzccf%V zwu5y$QIE%W?>>BY@7;TkzYm4{2-?^0&87b;BJ?-<&}u@n^LULx=o6$OmC;d#i8C2C z&Stnc$50-tdop~Sclw;}&G_QJj1U)`XOAvs{BeIK5D#R6@n9wt4>^2Z4`+JfJq%(I zW>l~0OEI^EFIa?b!ryK2URAt?;(fSxt-rNhi^peQdELlcnApBkIES;UX@|~SxG?+jy9&-( znvsn%w(mSPP1S;v=U%OK}1*}_qeExH5e@|GQRyC(^TAzN@HC_muz zTsEKvo2aiEimVqrF*&E%B5uN(^j$fEliIts$C6W~(y<-L0}p%P|M=fP&LRdJ&e|{{ zLfvLtKq4xli|7-Efjf22t*v_ogR*<}Ad9}G+4dq5ab|NZPUn5|>_v11z019ct}yh? zYaKZ4S)1pm=sYax`%1U)qs&Z{Cn12fy^4{^XqGK*Sk4xRq3d(9^0v(@x?y72bXJ`+ z=?+NW%>a$1jSP;-s;udjk&XT3Dws@Cv2=mRbF!W`Vv1%FO-Yz(jB_y+U$(GmfuV9S z;`odi+pyPUZr=8$u$55f$Ou3O;BQ_9@(=ix(0y@eY1hiG(uoG^;|J>D-m*Y{b-{mK zTo#uCD}gfnp4<-%HW15)+k{F4mIF(nl~6fU7eWmlhMzon(%_Id*zhAUa9vmymi#OJ zvh#aVW;+=mY3=|mJ{ox*{gF97j&4fF1Kc+O4!YlrM~?r5D+sVK>eD7{PncjcXJk#B z$QUZtZLd2-A^@Rmgd2FEen9_^ZvpA{z9M>S!^dth-3NDpEi!AJI5697MYdZKyp37= zUfu?B+j`hGh%6j^wvg~k&|b$W&4hDrO0ty2SEQCD!TxkNq$_Ehg`vEOi3A5!Hzg~L z+fqqlWMJ6LVMR-7ij>q1gG@Teu#dd+2(Iaa)sZnEHYckp<>$?`?Sq32JDrYlgc`@$ zUYjFWG3kmyIWkPAgcK))B$`I}0r046nWur2(06_m==EoHjGgqgtO_!$W!}Q{G*}Qu0+PTuXdLUeyehf&c;EkCyirKt# z>#{T5S*p|HJ3yXpXR!H(Rl(-aZzm_m<`iQ~+r~mqvE}Ay0XFBizyV_$M91d&6bv@a zt{6n^Hs-gb- z>l)naGNWEXYXgTz#%b>vN_J7Q8wmKo9@?izj7UJbeIVHM0Y@z|0pk?IoCe~$z{o28 z^zGZ@U+u5>k5&E0YW`#YyBm;a0j}VW$yr&Sw=~6kbPyPisL^a**PO6wrK!gnx~g>A zl3Ia2KbC-gLgP0)Y;D$tcR+T6=+G`XwvPs#@^5XtZ~JrJ?HilWseW5paZ^-U^KKCV zMmO#PSLDn-tEVlgGAetE>-P0UCWRI}MeY>(<<|@RQ+K1cjj~B!C@Z%%t3$rM8%4g| zf7^+Go3J|eyp6d%*8j1yPAS~Rb$grk%vCI^SBBNe!oJy#Wr8%-UxO^G&{|ub)3HS4Yzj|8J>4r`#zaoxgbG<&&MKHZ2QRZj z84U`Q7NYITKxGb6(LS4p_mX)h*4hGucuW#=^R{SdAY#dx93)^aH#a)i@@g0e8%QT{I8_ZDsfCU_2n{V?x{<6!ChkR^yBm3~8rffq z?5~Cn)ItXyg!-3%vGm5u8=!*N0}G)lu;RY^!#h@|K7DC@{MPf;orkN#Q?=o#@|k*Y z=w5K_Zg6b%QZ+bH3r|lLpSABGRUA#N`V*SKS<+b?T6Y&aM14rR% zAS5%>Y(wOO-Uf0B?{)IyOmxKdC1tR5P9roY5gK$IKeEH-JG!hZX`Gp#G)RWfgkleZ z`%F4@v^m+TE={*0q!ocZM8{1!N$wP&Zb4{tCG;DD(0FM_sHiH#8Ieug0p@=BxE~|V zXNTR42SF6lu zjTx;^99T=OCqFYjH~xI0@@Bk&#u;!iAgiDk-0mpOt#bZid&ucgHFp3)gKf=l*p4Ot E12)1tG5`Po literal 0 HcmV?d00001 diff --git a/app/routers/elevators.py b/app/routers/elevators.py new file mode 100644 index 0000000..58583a8 --- /dev/null +++ b/app/routers/elevators.py @@ -0,0 +1,88 @@ +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session + +from app.models.elevators import Demand, ElevatorState +from app.schemas.elevators import DemandOut, DemandCreate, StateOut, StateCreate +from app.database.elevators import SessionLocal + +router = APIRouter(prefix="/api", tags=["elevators"]) + + +def get_db(): + db = SessionLocal() + try: + yield db + db.commit() + except Exception: + db.rollback() + raise + finally: + db.close() + + +# POST /demands +@router.post("/demands", response_model=DemandOut) +def create_demand(demand: DemandCreate, db: Session = Depends(get_db)): + """ + Registers a new elevator demand when a user calls the elevator from a specific floor. + """ + rec = Demand(floor=demand.floor) + db.add(rec) + db.flush() + db.refresh(rec) + return rec + + +# POST /state +@router.post("/state", response_model=StateOut) +def create_state(state: StateCreate, db: Session = Depends(get_db)): + """ + Records the elevator’s current state, including its current floor and whether it is vacant. + """ + rec = ElevatorState(floor=state.floor, vacant=state.vacant) + db.add(rec) + db.flush() + db.refresh(rec) + return rec + + +# GET /analytics +@router.get("/analytics") +def analyze_positioning(db: Session = Depends(get_db), limit: int = 50, threshold: int = 3): + """ + Analyzes elevator positioning efficiency by comparing resting positions with upcoming demands. + """ + states = ( + db.query(ElevatorState) + .order_by(ElevatorState.timestamp.asc()) + .limit(limit) + .all() + ) + + if not states: + raise HTTPException(status_code=404, detail="No elevator states found.") + + distances = [] + + for state in states: + demand = ( + db.query(Demand) + .filter(Demand.timestamp > state.timestamp) + .order_by(Demand.timestamp.asc()) + .first() + ) + if demand: + distance = abs(demand.floor - state.floor) + distances.append(distance) + + if not distances: + raise HTTPException(status_code=404, detail="Not enough state → demand pairs found.") + + avg_distance = sum(distances) / len(distances) + + return { + "average_distance": round(avg_distance, 2), + "threshold": threshold, + "mispositioned": avg_distance > threshold, + "message": "Elevator is frequently poorly positioned." if avg_distance > threshold else "Positioning is within acceptable range." + } diff --git a/app/schemas/__init__.py b/app/schemas/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/schemas/__pycache__/__init__.cpython-311.pyc b/app/schemas/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e04b73286c6ce71f766fe1da150a3fdb707cae94 GIT binary patch literal 189 zcmZ3^%ge<81l36i86f&Gh=2h`DC095kTIPhg&~+hlhJP_LlF~@{~09t)fY(UXXNLm z>L;coX6BUS=jk7AEJ{sONJ&))DN0OA%*n{tPtGhU%1n+g&PYuy&`(J%D@iRb(N8QW z&@WETNX<*&hQRUlRkL-$ zAmlqf>>Zgu!OmL0AQ16>+A@GUB2v%bmFM<(=|n*=w;-c3?yVVwG{Jpi1MpNb_98p7Ps?FQYWG z5=g0Pklbp$vt_rKTSxY0-6A6v>2Ps@Qcuk{#g4 z&l*6rB|}kVocTc_qw1G+D7p*4(o-kU3Zs}9$eMhH}C zFU^=cdz)tGGSG+J5vH&>MF+qOiyPVtlUqJ*fHUBUo#sZKJnAH=;qx?y+aXR^*iMTp zN`@{=KxVT8>XvICvnr1T6cA<+jv*8gu!gh%eHVf2$!NuYu8!lluJ{B-b9>-GuyX}q ztOZWKyVzY^UmO(Y`xahX*8I>Wxigzrho&)ScOT$6%7>|r?!X0L{@npkZl(FhV7wS@ z#?hv*SDDA6q*k&2sx!5kQU;UK&cdn+0oBIm0mh`|k9C*2OY2L6!g18Fen<&UI$Ftp zcz0ys5Ao6z&-Rb%rLTbU@Da$FBjo*gxtT+LS({Lvl3su5W8*_b_kU r)E<&)=oPkcboYQdVbIc$Ohd1-jib8F%8y(Z literal 0 HcmV?d00001 diff --git a/app/schemas/elevators.py b/app/schemas/elevators.py new file mode 100644 index 0000000..9ee91ab --- /dev/null +++ b/app/schemas/elevators.py @@ -0,0 +1,27 @@ +from datetime import datetime +from pydantic import BaseModel, Field + + +class DemandCreate(BaseModel): + floor: int = Field(..., ge=0, description="Andar onde o usuário chamou o elevador") + +class DemandOut(DemandCreate): + id: int + timestamp: datetime + + model_config = { + "from_attributes": True # substitui orm_mode no Pydantic v2 + } + + +class StateCreate(BaseModel): + floor: int + vacant: bool + +class StateOut(StateCreate): + id: int + timestamp: datetime + + model_config = { + "from_attributes": True + } \ No newline at end of file diff --git a/chatgpt/app_tests.py b/chatgpt/app_tests.py deleted file mode 100644 index 258a8a6..0000000 --- a/chatgpt/app_tests.py +++ /dev/null @@ -1,10 +0,0 @@ -def test_create_demand(client): - response = client.post('/demand', json={'floor': 3}) - assert response.status_code == 201 - assert response.get_json() == {'message': 'Demand created'} - - -def test_create_state(client): - response = client.post('/state', json={'floor': 5, 'vacant': True}) - assert response.status_code == 201 - assert response.get_json() == {'message': 'State created'} diff --git a/chatgpt/db.sql b/chatgpt/db.sql deleted file mode 100644 index 1555ffe..0000000 --- a/chatgpt/db.sql +++ /dev/null @@ -1,12 +0,0 @@ -CREATE TABLE elevator_demands ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - floor INTEGER -); - -CREATE TABLE elevator_states ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - floor INTEGER, - vacant BOOLEAN -); diff --git a/chatgpt/main.py b/chatgpt/main.py deleted file mode 100644 index 7f97d98..0000000 --- a/chatgpt/main.py +++ /dev/null @@ -1,43 +0,0 @@ -from flask import Flask, request, jsonify -from flask_sqlalchemy import SQLAlchemy -from datetime import datetime - -app = Flask(__name__) -app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///elevator.db' -db = SQLAlchemy(app) - - -class ElevatorDemand(db.Model): - id = db.Column(db.Integer, primary_key=True) - timestamp = db.Column(db.DateTime, default=datetime.utcnow) - floor = db.Column(db.Integer, nullable=False) - - -class ElevatorState(db.Model): - id = db.Column(db.Integer, primary_key=True) - timestamp = db.Column(db.DateTime, default=datetime.utcnow) - floor = db.Column(db.Integer, nullable=False) - vacant = db.Column(db.Boolean, nullable=False) - - -@app.route('/demand', methods=['POST']) -def create_demand(): - data = request.get_json() - new_demand = ElevatorDemand(floor=data['floor']) - db.session.add(new_demand) - db.session.commit() - return jsonify({'message': 'Demand created'}), 201 - - -@app.route('/state', methods=['POST']) -def create_state(): - data = request.get_json() - new_state = ElevatorState(floor=data['floor'], vacant=data['vacant']) - db.session.add(new_state) - db.session.commit() - return jsonify({'message': 'State created'}), 201 - - -if __name__ == '__main__': - db.create_all() - app.run(debug=True) diff --git a/chatgpt/requirements.txt b/chatgpt/requirements.txt deleted file mode 100644 index 14d1bb0..0000000 --- a/chatgpt/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -Flask==2.0.2 -Flask-SQLAlchemy==2.5.1 -pytest==6.2.5 -pytest-flask==1.2.0 diff --git a/elevators.db b/elevators.db new file mode 100644 index 0000000000000000000000000000000000000000..57fdc6926fa7fcc7421b33fb5b4d114a08d83390 GIT binary patch literal 24576 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 literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_endpoints.cpython-311-pytest-8.4.1.pyc b/tests/__pycache__/test_endpoints.cpython-311-pytest-8.4.1.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6acf88ca25741b5201d7529f90cf255d70307c17 GIT binary patch literal 9885 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 literal 0 HcmV?d00001 diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py new file mode 100644 index 0000000..564bfbb --- /dev/null +++ b/tests/test_endpoints.py @@ -0,0 +1,40 @@ +from fastapi.testclient import TestClient +from app.main import app + +client = TestClient(app) + + +def test_create_demand(): + resp = client.post("/api/demands", json={"floor": 3}) + assert resp.status_code == 200 + payload = resp.json() + assert payload["floor"] == 3 + assert "id" in payload + assert "timestamp" in payload + + +def test_create_state(): + resp = client.post("/api/state", json={"floor": 2, "vacant": True}) + assert resp.status_code == 200 + payload = resp.json() + assert payload["floor"] == 2 + assert payload["vacant"] is True + assert "timestamp" in payload + + +def test_analyze_positioning(): + # Criar um state antigo + client.post("/api/state", json={"floor": 1, "vacant": True}) + + # Criar uma demanda posterior + client.post("/api/demands", json={"floor": 5}) + + # Chamar a rota de análise + resp = client.get("/api/analytics", params={"limit": 10, "threshold": 2}) + assert resp.status_code == 200 + data = resp.json() + + assert "average_distance" in data + assert "mispositioned" in data + assert isinstance(data["average_distance"], float) + assert isinstance(data["mispositioned"], bool) \ No newline at end of file