This commit is contained in:
DmitryGantimurov 2023-07-17 23:36:46 +03:00
commit 91842dcc51
9 changed files with 115 additions and 100 deletions

View File

@ -1,4 +1,7 @@
<<<<<<< HEAD
# <<<<<<< HEAD
=======
>>>>>>> de8a1abcbfea61d8d4898c18e133b8b0feaf87e8
#подключение библиотек
from fastapi import FastAPI, Response, Path, Depends, Body, Form, Query, status, HTTPException, APIRouter, UploadFile, File
from fastapi.responses import HTMLResponse, FileResponse, JSONResponse, RedirectResponse
@ -102,7 +105,7 @@ def put_in_db(name: Annotated[str, Form()], category: Annotated[str, Form()], be
uploaded_name = "/uploads/"+destination.name
temp_ancmt = Announcement(user_id=userId, name=name, category=category, best_by=bestBy, address=address, longtitude=longtitude, latitude=latitude, description=description, src=uploaded_name, metro=metro, trashId=trashId)
temp_ancmt = Announcement(user_id=userId, name=name, category=category, best_by=bestBy, address=address, longtitude=longtitude, latitude=latitude, description=description, src=uploaded_name, metro=metro, trashId=trashId, booked_by=-1)
db.add(temp_ancmt) # добавляем в бд
db.commit() # сохраняем изменения
db.refresh(temp_ancmt) # обновляем состояние объекта
@ -185,7 +188,7 @@ async def read_own_items(
@app.get("/api/trashbox")
def get_trashboxes(lat:float, lng:float):#крутая функция для работы с api
BASE_URL='https://geointelect2.gate.petersburg.ru'#адрес сайта и мой токин
my_token='eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhU1RaZm42bHpTdURYcUttRkg1SzN5UDFhT0FxUkhTNm9OendMUExaTXhFIn0.eyJleHAiOjE3Nzg2NTk4MjEsImlhdCI6MTY4Mzk2NTQyMSwianRpIjoiOTI2ZGMyNmEtMGYyZi00OTZiLWI0NTUtMWQyYWM5YmRlMTZkIiwiaXNzIjoiaHR0cHM6Ly9rYy5wZXRlcnNidXJnLnJ1L3JlYWxtcy9lZ3MtYXBpIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImJjYjQ2NzljLTU3ZGItNDU5ZC1iNWUxLWRlOGI4Yzg5MTMwMyIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFkbWluLXJlc3QtY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6IjE2MGU1ZGVkLWFmMjMtNDkyNS05OTc1LTRhMzM0ZjVmNTkyOSIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiLyoiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMtZWdzLWFwaSIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6ImVtYWlsIHByb2ZpbGUiLCJzaWQiOiIxNjBlNWRlZC1hZjIzLTQ5MjUtOTk3NS00YTMzNGY1ZjU5MjkiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsIm5hbWUiOiLQktC70LDQtNC40LzQuNGAINCv0LrQvtCy0LvQtdCyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZTBmYzc2OGRhOTA4MjNiODgwZGQzOGVhMDJjMmQ5NTciLCJnaXZlbl9uYW1lIjoi0JLQu9Cw0LTQuNC80LjRgCIsImZhbWlseV9uYW1lIjoi0K_QutC-0LLQu9C10LIifQ.BRyUIyY-KKnZ9xqTNa9vIsfKF0UN2VoA9h4NN4y7IgBVLiiS-j43QbeE6qgjIQo0pV3J8jtCAIPvJbO-Ex-GNkw_flgMiGHhKEpsHPW3WK-YZ-XsZJzVQ_pOmLte-Kql4z97WJvolqiXT0nMo2dlX2BGvNs6JNbupvcuGwL4YYpekYAaFNYMQrxi8bSN-R7FIqxP-gzZDAuQSWRRSUqVBLvmgRhphTM-FAx1sX833oXL9tR7ze3eDR_obSV0y6cKVIr4eIlKxFd82qiMrN6A6CTUFDeFjeAGERqeBPnJVXU36MHu7Ut7eOVav9OUARARWRkrZRkqzTfZ1iqEBq5Tsg'
my_token='eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhU1RaZm42bHpTdURYcUttRkg1SzN5UDFhT0FxUkhTNm9OendMUExaTXhFIn0.eyJleHAiOjE3ODM3ODk4NjgsImlhdCI6MTY4OTA5NTQ2OCwianRpIjoiNDUzNjQzZTgtYTkyMi00NTI4LWIzYmMtYWJiYTNmYjkyNTkxIiwiaXNzIjoiaHR0cHM6Ly9rYy5wZXRlcnNidXJnLnJ1L3JlYWxtcy9lZ3MtYXBpIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImJjYjQ2NzljLTU3ZGItNDU5ZC1iNWUxLWRlOGI4Yzg5MTMwMyIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFkbWluLXJlc3QtY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6ImM2ZDJiOTZhLWMxNjMtNDAxZS05ZjMzLTI0MmE0NDcxMDY5OCIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiLyoiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMtZWdzLWFwaSIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6ImVtYWlsIHByb2ZpbGUiLCJzaWQiOiJjNmQyYjk2YS1jMTYzLTQwMWUtOWYzMy0yNDJhNDQ3MTA2OTgiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsIm5hbWUiOiLQktC70LDQtNC40LzQuNGAINCv0LrQvtCy0LvQtdCyIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZTBmYzc2OGRhOTA4MjNiODgwZGQzOGVhMDJjMmQ5NTciLCJnaXZlbl9uYW1lIjoi0JLQu9Cw0LTQuNC80LjRgCIsImZhbWlseV9uYW1lIjoi0K_QutC-0LLQu9C10LIifQ.E2bW0B-c6W5Lj63eP_G8eI453NlDMnW05l11TZT0GSsAtGayXGaolHtWrmI90D5Yxz7v9FGkkCmcUZYy1ywAdO9dDt_XrtFEJWFpG-3csavuMjXmqfQQ9SmPwDw-3toO64NuZVv6qVqoUlPPj57sLx4bLtVbB4pdqgyJYcrDHg7sgwz4d1Z3tAeUfSpum9s5ZfELequfpLoZMXn6CaYZhePaoK-CxeU3KPBPTPOVPKZZ19s7QY10VdkxLULknqf9opdvLs4j8NMimtwoIiHNBFlgQz10Cr7bhDKWugfvSRsICouniIiBJo76wrj5T92s-ztf1FShJuqnQcKE_QLd2A'
head = {'Authorization': 'Bearer {}'.format(my_token)}
my_data={
@ -217,6 +220,7 @@ def get_trashboxes(lat:float, lng:float):#крутая функция для р
@app.get("/{rest_of_path:path}")
async def react_app(req: Request, rest_of_path: str):
return templates.TemplateResponse('index.html', { 'request': req })
<<<<<<< HEAD
# =======
# #подключение библиотек
# from fastapi import FastAPI, Response, Path, Depends, Body, Form, Query, status, HTTPException, APIRouter, UploadFile, File
@ -437,3 +441,5 @@ async def react_app(req: Request, rest_of_path: str):
# async def react_app(req: Request, rest_of_path: str):
# return templates.TemplateResponse('index.html', { 'request': req })
# >>>>>>> 3668e8c33f71b7a79a0c83d41a106d9b55e2df71
=======
>>>>>>> de8a1abcbfea61d8d4898c18e133b8b0feaf87e8

View File

@ -0,0 +1,12 @@
import { API_URL } from '../../config'
import { PutAnnouncement, PutAnnouncementResponse } from './types'
const composePutAnnouncementURL = () => (
API_URL + '/announcement?'
)
const processPutAnnouncement = (data: PutAnnouncementResponse): PutAnnouncement => {
return data.Answer
}
export { composePutAnnouncementURL, processPutAnnouncement }

View File

@ -0,0 +1,17 @@
import { isObject } from '../../utils/types'
type PutAnnouncementResponse = {
Answer: boolean
}
const isPutAnnouncementResponse = (obj: unknown): obj is PutAnnouncementResponse => (
isObject(obj, {
'Answer': 'boolean'
})
)
type PutAnnouncement = boolean
export type { PutAnnouncementResponse, PutAnnouncement }
export { isPutAnnouncementResponse }

View File

@ -1,6 +1,7 @@
import { LatLng } from 'leaflet'
import { API_URL } from '../../config'
import { Trashbox, TrashboxResponse } from './types'
const composeTrashboxURL = (position: LatLng) => (
API_URL + '/trashbox?' + new URLSearchParams({
@ -9,4 +10,7 @@ const composeTrashboxURL = (position: LatLng) => (
}).toString()
)
export { composeTrashboxURL }
const processTrashbox = (data: TrashboxResponse): Trashbox[] =>
data
export { composeTrashboxURL, processTrashbox }

View File

@ -1,79 +1,31 @@
import { useEffect, useRef, useState } from 'react'
import { useCallback } from 'react'
import { API_URL } from '../../config'
import { isLiteralUnion } from '../../utils/types'
import { handleHTTPErrors } from '../../utils'
const addErrors = ['Не удалось опубликовать объявление', 'Неверный ответ от сервера', 'Неизвестная ошибка'] as const
type AddError = typeof addErrors[number]
const isAddError = (obj: unknown): obj is AddError => (
isLiteralUnion(obj, addErrors)
)
const buttonStates = ['Опубликовать', 'Загрузка...', 'Опубликовано', 'Отменено'] as const
type ButtonState = typeof buttonStates[number] | AddError
type AddResponse = {
Answer: boolean
}
const isAddResponse = (obj: unknown): obj is AddResponse => (
typeof obj === 'object' && obj !== null && typeof Reflect.get(obj, 'Answer') === 'boolean'
)
import { useSend } from '..'
import { composePutAnnouncementURL, processPutAnnouncement } from '../../api/putAnnouncement'
import { isPutAnnouncementResponse } from '../../api/putAnnouncement/types'
import useSendButtonCaption from '../useSendButtonCaption'
const useAddAnnouncement = () => {
const [status, setStatus] = useState<ButtonState>('Опубликовать')
const { doSend, loading, error } = useSend(
composePutAnnouncementURL(),
'PUT',
true,
isPutAnnouncementResponse,
processPutAnnouncement,
)
const timerIdRef = useRef<number>()
const abortControllerRef = useRef<AbortController>()
const { update, ...button } = useSendButtonCaption('Опубликовать', loading, error, 'Опубликовано')
const doAdd = async (formData: FormData) => {
if (status === 'Загрузка...') {
abortControllerRef.current?.abort()
setStatus('Отменено')
timerIdRef.current = setTimeout(() => setStatus('Опубликовать'), 3000)
return
}
setStatus('Загрузка...')
const abortController = new AbortController()
abortControllerRef.current = abortController
try {
const res = await fetch(API_URL + '/announcement', {
method: 'PUT',
body: formData,
signal: abortController.signal
const doSendWithButton = useCallback(async (formData: FormData) => {
const data = await doSend({}, {
body: formData
})
update(data)
handleHTTPErrors(res)
return data
}, [doSend, update])
const data: unknown = await res.json()
if (!isAddResponse(data)) throw new Error('Неверный ответ от сервера')
if (!data.Answer) {
throw new Error('Не удалось опубликовать объявление')
}
setStatus('Опубликовано')
} catch (err) {
setStatus(isAddError(err) ? err : 'Неизвестная ошибка')
timerIdRef.current = setTimeout(() => setStatus('Опубликовать'), 10000)
}
}
useEffect(() => {
const abortController = abortControllerRef.current
return () => {
clearTimeout(timerIdRef.current)
abortController?.abort()
}
})
return { doAdd, status }
return { doSend: doSendWithButton, button }
}
export default useAddAnnouncement

View File

@ -1,7 +1,7 @@
import { LatLng } from 'leaflet'
import { useFetch } from '../'
import { composeTrashboxURL } from '../../api/trashbox'
import { composeTrashboxURL, processTrashbox } from '../../api/trashbox'
import { isTrashboxResponse } from '../../api/trashbox/types'
const useTrashboxes = (position: LatLng) => (
@ -10,7 +10,7 @@ const useTrashboxes = (position: LatLng) => (
'GET',
true,
isTrashboxResponse,
(data) => data,
processTrashbox,
[]
)
)

View File

@ -24,6 +24,7 @@ function useSend<R, T>(
/** Don't use in useEffect. If you need request result, go with useFetch instead */
const doSend = useCallback(async (urlProps?: Record<string, string>, params?: Omit<RequestInit, 'method'>) => {
setLoading(true)
setError(null)
if (abortControllerRef.current) {
abortControllerRef.current.abort()

View File

@ -0,0 +1,44 @@
import { useCallback, useEffect, useState } from 'react'
function useSendButtonCaption(
initial: string,
loading: boolean,
error: string | null,
result = initial,
singular = true
) {
const [caption, setCaption] = useState(initial)
const [disabled, setDisabled] = useState(false)
const [title, setTitle] = useState(initial)
const update = useCallback(<T extends NonNullable<unknown>>(data: T | undefined) => {
if (data !== undefined) {
setCaption(result)
setTitle('Отправить ещё раз')
if (singular) {
setDisabled(true)
setTitle('')
}
}
}, [result, singular])
useEffect(() => {
if (loading) {
setCaption('Загрузка...')
setTitle('Отменить и отправить ещё раз')
}
}, [loading])
useEffect(() => {
if (!loading && error !== null) {
setCaption(error + ', нажмите, чтобы попробовать ещё раз')
setTitle('')
}
}, [error, loading])
return { update, children: caption, disabled, title }
}
export default useSendButtonCaption

View File

@ -1,11 +1,10 @@
import { CSSProperties, FormEventHandler, useEffect, useState } from 'react'
import { CSSProperties, FormEventHandler, useState } from 'react'
import { Form, Button, Card } from 'react-bootstrap'
import { MapContainer, TileLayer } from 'react-leaflet'
import { latLng } from 'leaflet'
import { ClickHandler, LocationMarker, TrashboxMarkers } from '../components'
import { useAddAnnouncement, useTrashboxes } from '../hooks/api'
import { handleHTTPErrors } from '../utils'
import { categories, categoryNames } from '../assets/category'
import { stations, lines, lineNames } from '../assets/metro'
import { fallbackError, gotError } from '../hooks/useFetch'
@ -32,25 +31,7 @@ function AddPage() {
const address = useOsmAddresses(addressPosition)
useEffect(() => {
if (!gotError(address))
void (async () => {
try {
const res = await fetch(location.protocol + '//nominatim.openstreetmap.org/search?format=json&q=' + encodeURIComponent(address.data))
handleHTTPErrors(res)
const fetchData: unknown = await res.json()
console.log('f', fetchData)
} catch (err) {
console.error(err)
}
})()
}, [address])
const { doAdd, status } = useAddAnnouncement()
const { doSend, button } = useAddAnnouncement()
const handleSubmit: FormEventHandler<HTMLFormElement> = (event) => {
event.preventDefault()
@ -63,7 +44,7 @@ function AddPage() {
formData.append('address', address.data || '') // if address.error
formData.set('bestBy', new Date((formData.get('bestBy') as number | null) || 0).getTime().toString())
void doAdd(formData)
void doSend(formData)
}
return (
@ -151,7 +132,7 @@ function AddPage() {
</Form.Select>
</Form.Group>
<Form.Group className='mb-3' controlId='password'>
<Form.Group className='mb-3' controlId='trashbox'>
<Form.Label>Пункт сбора мусора</Form.Label>
<div className='mb-3'>
{trashboxes.loading
@ -195,9 +176,7 @@ function AddPage() {
)}
</Form.Group>
<Button variant='success' type='submit'>
{status}
</Button>
<Button variant='success' type='submit' {...button} />
</Form>
</Card.Body>
</Card>