Compare commits
5 Commits
main
...
af9aa243bf
Author | SHA1 | Date | |
---|---|---|---|
af9aa243bf | |||
c0229d6727 | |||
56b72c8a62 | |||
e42293df3f | |||
0e7dd90646 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -28,7 +28,6 @@ dist-ssr
|
||||
*.db
|
||||
uploads/
|
||||
.env
|
||||
poem_pic/
|
||||
|
||||
poem_pic/
|
||||
|
||||
|
@ -11,7 +11,6 @@ from . import auth_utils, orm_models, pydantic_schemas
|
||||
|
||||
# Загружаем стихи
|
||||
async def add_poems_to_db(async_db: AsyncSession):
|
||||
poems = []
|
||||
f1 = open('poems.txt', encoding='utf-8', mode='r')#открыть фаил для чтения на русском
|
||||
for a in range(1, 110):
|
||||
f1.seek(0)#перейти к началу
|
||||
@ -36,11 +35,8 @@ async def add_poems_to_db(async_db: AsyncSession):
|
||||
author += str1
|
||||
poem = orm_models.Poems(title=name, text=stixi, author=author)
|
||||
# В конце каждой итерации добавляем в базу данных
|
||||
poems.append(poem)
|
||||
|
||||
async_db.add_all(poems)
|
||||
|
||||
await async_db.commit()
|
||||
async_db.add(poem)
|
||||
async_db.commit()
|
||||
|
||||
# close the file
|
||||
f1.close()
|
||||
|
50
back/api.py
50
back/api.py
@ -98,7 +98,7 @@ async def put_in_db(name: Annotated[str, Form()], category: Annotated[str, Form(
|
||||
# создаем объект Announcement
|
||||
temp_ancmt = orm_models.Announcement(user_id=current_user.id, name=name, category=category, best_by=bestBy,
|
||||
address=address, longtitude=longtitude, latitude=latitude, description=description, metro=metro,
|
||||
trashId=trashId, src=uploaded_name, booked_by=0)
|
||||
trashId=trashId, src=uploaded_name, booked_counter=0)
|
||||
try:
|
||||
db.add(temp_ancmt) # добавляем в бд
|
||||
await db.commit() # сохраняем изменения
|
||||
@ -142,12 +142,25 @@ async def change_book_status(data: pydantic_schemas.Book, current_user: Annotate
|
||||
if current_user.id == announcement_to_change.user_id:
|
||||
raise HTTPException(status_code=403, detail="A user can't book his announcement")
|
||||
else:
|
||||
# Инкрементируем поле booked_by на 1
|
||||
announcement_to_change.booked_by += 1
|
||||
# фиксируем изменения в бд
|
||||
await db.commit()
|
||||
await db.refresh(announcement_to_change)
|
||||
return {"Success": True}
|
||||
# ищем пару с заданными id объявления и пользователя
|
||||
query = await db.execute(select(orm_models.AnnouncementUser).where(
|
||||
orm_models.AnnouncementUser.announcement_id == announcement_to_change.id).where(
|
||||
orm_models.AnnouncementUser.booking_user_id == current_user.id))
|
||||
pair_found = query.scalars().first()
|
||||
# если не найдена
|
||||
if not pair_found:
|
||||
# создаем новый объект таблицы AnnouncementUser
|
||||
new_pair = orm_models.AnnouncementUser(announcement_to_change.id, current_user.id)
|
||||
# Инкрементируем поле booked_counter на 1
|
||||
announcement_to_change.booked_counter += 1
|
||||
# вставляем индекс забронировавшего пользователя в поле booked_by
|
||||
db.add(new_pair)
|
||||
|
||||
# фиксируем изменения в бд
|
||||
await db.commit()
|
||||
await db.refresh(announcement_to_change)
|
||||
return {"Success": True}
|
||||
raise HTTPException(status_code=403, detail="The announcement is already booked by this user")
|
||||
|
||||
|
||||
# reginstration
|
||||
@ -200,6 +213,21 @@ def read_users_me(current_user: Annotated[pydantic_schemas.User, Depends(auth_ut
|
||||
return current_user
|
||||
|
||||
|
||||
# ендпоинт для генерации refresh token. Генерируется при каждом входе юзера
|
||||
# на сайт
|
||||
@app.post("/api/token/refresh", response_model=pydantic_schemas.Token)
|
||||
async def generate_refresh_token(
|
||||
current_user: Annotated[pydantic_schemas.User, Depends(auth_utils.get_current_active_user)]
|
||||
):
|
||||
# задаем временной интервал, в течение которого токен можно использовать
|
||||
access_token_expires = auth_utils.timedelta(minutes=auth_utils.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
# создаем новый токен
|
||||
access_token = auth_utils.create_access_token(
|
||||
data={"user_id": current_user.id}, expires_delta=access_token_expires
|
||||
)
|
||||
return {"access_token":access_token}
|
||||
|
||||
|
||||
# изменяем рейтинг пользователя
|
||||
@app.post("/api/user/rating")
|
||||
async def add_points(data: pydantic_schemas.AddRating, current_user: Annotated[pydantic_schemas.User, Depends(auth_utils.get_current_user)], db: Annotated[Session, Depends(auth_utils.get_session)]):
|
||||
@ -267,18 +295,12 @@ async def get_trashboxes(data: pydantic_schemas.TrashboxRequest = Depends()): #
|
||||
'limit' : '1'
|
||||
}
|
||||
# Перевод категории с фронта на категорию с сайта
|
||||
try:
|
||||
list_of_category = trashboxes_category[data.Category]
|
||||
except:
|
||||
list_of_category = trashboxes_category['other_things']
|
||||
list_of_category = trashboxes_category[data.Category]
|
||||
|
||||
# Получение ответа от стороннего апи
|
||||
response = requests.post(TRASHBOXES_BASE_URL + "/nearest_recycling/get", headers=head, data=my_data, timeout=10)
|
||||
infos = response.json()
|
||||
|
||||
if 'error' in infos and infos['error_description'] == 'Invalid bearer token':
|
||||
raise HTTPException(status_code=502, detail="Invalid trashboxes token")
|
||||
|
||||
# Чтение ответа
|
||||
trashboxes = []
|
||||
for trashbox in infos["results"]:
|
||||
|
@ -21,7 +21,7 @@ async def main():
|
||||
|
||||
await init_models()
|
||||
|
||||
server = Server(config=uvicorn.Config(app_fastapi, workers=1, loop="asyncio", host="0.0.0.0"))
|
||||
server = Server(config=uvicorn.Config(app_fastapi, workers=1, loop="asyncio", host="127.0.0.1"))
|
||||
|
||||
api = asyncio.create_task(server.serve())
|
||||
sched = asyncio.create_task(app_rocketry.serve())
|
||||
|
@ -1,9 +1,10 @@
|
||||
from sqlalchemy import Column, Integer, String, Boolean, Float, Date, ForeignKey
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy import Column, Integer, String, Boolean, Float, Date, ForeignKey, ForeignKeyConstraint
|
||||
from sqlalchemy.orm import relationship, mapped_column, composite, Mapped
|
||||
from sqlalchemy.dialects import postgresql
|
||||
import dataclasses
|
||||
|
||||
from .db import Base, engine
|
||||
|
||||
|
||||
class User(Base):#класс пользователя
|
||||
__tablename__ = "users"
|
||||
|
||||
@ -18,7 +19,7 @@ class User(Base):#класс пользователя
|
||||
num_of_ratings = Column(Integer, default=0) # количество оценок (т.е. то, сколько раз другие пользователи оценили текущего)
|
||||
reg_date = Column(Date) # дата регистрации
|
||||
|
||||
announcements = relationship("Announcement", back_populates="user", lazy='selectin')
|
||||
announcements = relationship("Announcement", secondary="announcementuser", back_populates="user", lazy='selectin')
|
||||
trashboxes_chosen = relationship("Trashbox", back_populates="user", lazy='selectin')
|
||||
|
||||
class Announcement(Base): #класс объявления
|
||||
@ -36,11 +37,35 @@ class Announcement(Base): #класс объявления
|
||||
src = Column(String, nullable=True) #изображение продукта в объявлении
|
||||
metro = Column(String) #ближайщее метро от адреса нахождения продукта
|
||||
trashId = Column(Integer, nullable=True)
|
||||
booked_by = Column(Integer) #количество забронировавших (0 - никто не забронировал)
|
||||
# state = Column(Enum(State), default=State.published) # состояние объявления (опубликовано, забронировано, устарело)
|
||||
booked_by = Column(Integer, nullable=True) # id пользователя, забронировавшего объявление
|
||||
booked_counter = Column(Integer, nullable=True) #количество забронировавших (0 - никто не забронировал)
|
||||
obsolete = Column(Boolean, default=False) # состояние объявления (по-умолчанию считаем его актуальным)
|
||||
|
||||
user = relationship("User", back_populates="announcements")
|
||||
user = relationship("User", secondary="announcementuser", back_populates="announcements")
|
||||
|
||||
# Класс пары, хранящей id объявления и id забронировавшего юзера
|
||||
@dataclasses.dataclass
|
||||
class UserAnouncementPair:
|
||||
announcement_id: int
|
||||
booking_user_id: int
|
||||
|
||||
class AnnouncementUser(Base):
|
||||
__tablename__ = "announcementuser"
|
||||
|
||||
def __init__(self, an_id, b_u_id):
|
||||
self.announcement_id = an_id
|
||||
self.booking_user_id = b_u_id
|
||||
|
||||
announcement_id = Column(Integer, ForeignKey("announcements.id"), primary_key=True) # id забронированного объявления
|
||||
booking_user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) # id пользователя, забронировавшего объявление
|
||||
|
||||
|
||||
|
||||
# class Ratings(Base):
|
||||
# __tablename__ = "ratings"
|
||||
|
||||
# rated_user_id = Column(Integer, primary_key=True) # id пользователя, оставившего оценку
|
||||
# rating_value = Column(Integer, primary_key=True) # оценка
|
||||
|
||||
|
||||
class Trashbox(Base): #класс мусорных баков
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 17 KiB |
Binary file not shown.
Before Width: | Height: | Size: 11 KiB |
Binary file not shown.
Before Width: | Height: | Size: 25 KiB |
@ -105,7 +105,5 @@ const lineByName = (name: string) => (
|
||||
lines.find(line => stations[line].has(name))
|
||||
)
|
||||
|
||||
const DEFAULT_LINE = 'Петроградская'
|
||||
|
||||
export type { Lines }
|
||||
export { lines, stations, colors, lineNames, lineByName, DEFAULT_LINE }
|
||||
export { lines, stations, colors, lineNames, lineByName }
|
||||
|
@ -1,73 +0,0 @@
|
||||
import { iconDormitory, iconITMO, iconLETI } from '../utils/markerIcons'
|
||||
|
||||
type LocationType = 'dormitory' | 'leti' | 'itmo'
|
||||
|
||||
const studentLocations: {
|
||||
name: string,
|
||||
position: [number, number],
|
||||
type: LocationType
|
||||
}[] = [
|
||||
{
|
||||
name: 'Первое, второе, третье общежития',
|
||||
position: [59.987299, 30.330672],
|
||||
type: 'dormitory',
|
||||
},
|
||||
{
|
||||
name: 'Четвертое общежитие',
|
||||
position: [59.985620, 30.331319],
|
||||
type: 'dormitory',
|
||||
},
|
||||
{
|
||||
name: 'Шестое общежитие',
|
||||
position: [59.969713, 30.299851],
|
||||
type: 'dormitory',
|
||||
},
|
||||
{
|
||||
name: 'Седьмое общежитие',
|
||||
position: [60.003723, 30.287616],
|
||||
type: 'dormitory',
|
||||
},
|
||||
{
|
||||
name: 'Восьмое общежитие',
|
||||
position: [59.991115, 30.318752],
|
||||
type: 'dormitory',
|
||||
},
|
||||
{
|
||||
name: 'Общежития Межвузовского студенческого городка',
|
||||
position: [59.871053, 30.307154],
|
||||
type: 'dormitory',
|
||||
},
|
||||
{
|
||||
name: 'Одиннадцатое общежитие',
|
||||
position: [59.877962, 30.242889],
|
||||
type: 'dormitory',
|
||||
},
|
||||
{
|
||||
name: 'Общежитие Академии транспортных технологий',
|
||||
position: [59.870375, 30.308646],
|
||||
type: 'dormitory',
|
||||
},
|
||||
{
|
||||
name: 'ЛЭТИ шестой корпус',
|
||||
position: [59.971578, 30.296653],
|
||||
type: 'leti',
|
||||
},
|
||||
{
|
||||
name: 'ЛЭТИ Первый и другие корпуса',
|
||||
position: [59.971947, 30.324303],
|
||||
type: 'leti',
|
||||
},
|
||||
{
|
||||
name: 'ИТМО',
|
||||
position: [59.956363, 30.310029],
|
||||
type: 'itmo',
|
||||
},
|
||||
]
|
||||
|
||||
const locationsIcons: Record<LocationType, L.Icon> = {
|
||||
dormitory: iconDormitory,
|
||||
itmo: iconITMO,
|
||||
leti: iconLETI,
|
||||
}
|
||||
|
||||
export { studentLocations, locationsIcons }
|
@ -11,7 +11,6 @@ import { iconItem } from '../utils/markerIcons'
|
||||
import { useId } from '../hooks'
|
||||
import SelectDisposalTrashbox from './SelectDisposalTrashbox'
|
||||
import StarRating from './StarRating'
|
||||
import StudentLocations from './StudentLocations'
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
@ -53,8 +52,6 @@ const View = ({
|
||||
url='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
|
||||
/>
|
||||
|
||||
<StudentLocations />
|
||||
|
||||
<Marker icon={iconItem} position={[lat, lng]}>
|
||||
<Popup>
|
||||
{address}
|
||||
|
@ -47,13 +47,18 @@ const AuthForm = ({ goBack }: AuthFormProps) => {
|
||||
<Form.Control placeholder='Пароль' name='password' type='password' required />
|
||||
</Form.Group>
|
||||
|
||||
<p>
|
||||
Нажимая на кнопку, я даю своё согласие на обработку персональных данных и соглашаюсь с{' '}
|
||||
<a
|
||||
href={`${document.location.origin}/privacy_policy.pdf`}
|
||||
target='_blank'
|
||||
>условиями политики конфиденциальности</a>
|
||||
</p>
|
||||
<Form.Group className='mb-3' controlId='privacyPolicyConsent'>
|
||||
<Form.Check>
|
||||
<Form.Check.Input type='checkbox' required />
|
||||
<Form.Check.Label>
|
||||
Я согласен с{' '}
|
||||
<a
|
||||
href={`${document.location.origin}/privacy_policy.pdf`}
|
||||
target='_blank'
|
||||
>условиями обработки персональных данных</a>
|
||||
</Form.Check.Label>
|
||||
</Form.Check>
|
||||
</Form.Group>
|
||||
|
||||
<ButtonGroup className='d-flex'>
|
||||
<Button
|
||||
|
@ -77,7 +77,7 @@ function Filters({ filter, setFilter, filterShown, setFilterShown }: FiltersProp
|
||||
</Form.Group>
|
||||
|
||||
<Button variant='success' type='submit'>
|
||||
Выбрать
|
||||
Отправить
|
||||
</Button>
|
||||
</Form>
|
||||
</Modal.Body>
|
||||
|
@ -24,10 +24,9 @@ function LocationMarker({ address, position, setPosition }: LocationMarkerProps)
|
||||
})
|
||||
|
||||
return (
|
||||
<Marker icon={iconItem} position={position} zIndexOffset={1000}>
|
||||
<Marker icon={iconItem} position={position}>
|
||||
<Popup>
|
||||
{address}
|
||||
<br />
|
||||
{position.lat.toFixed(4)}, {position.lng.toFixed(4)}
|
||||
</Popup>
|
||||
</Marker>
|
||||
|
@ -26,7 +26,7 @@ function Poetry() {
|
||||
}}
|
||||
/>
|
||||
<p><em>{poetry.data.author}</em></p>
|
||||
<img className={styles.image} src={`/poem_pic/${poetry.data.id}.jpg`} alt='Иллюстрация' />
|
||||
<img src={`/poem_pic/${poetry.data.id}.jpg`} alt='Иллюстрация' />
|
||||
</>
|
||||
)
|
||||
) : (
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Link } from 'react-router-dom'
|
||||
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
|
||||
import { Button } from 'react-bootstrap'
|
||||
|
||||
import { UserCategory, composeUserCategoriesFilters, userCategoriesInfos } from '../assets/userCategories'
|
||||
import { Announcement } from '../api/announcement/types'
|
||||
@ -95,9 +96,9 @@ function StoriesPreviewCarousel({ announcements, category }: StoriesPreviewProps
|
||||
|
||||
return <div className={styles.container}>
|
||||
{showScrollButtons.left &&
|
||||
<button onClick={doScroll(false)} className={`${styles.scrollButton} ${styles.leftScrollButton}`}>
|
||||
<Button onClick={doScroll(false)} className={`${styles.scrollButton} ${styles.leftScrollButton}`}>
|
||||
<img src={rightAngleIcon} alt='Показать ещё' />
|
||||
</button>
|
||||
</Button>
|
||||
}
|
||||
|
||||
<ul className={styles.list} ref={ulElement}>
|
||||
@ -105,9 +106,9 @@ function StoriesPreviewCarousel({ announcements, category }: StoriesPreviewProps
|
||||
</ul>
|
||||
|
||||
{showScrollButtons.right &&
|
||||
<button onClick={doScroll(true)} className={`${styles.scrollButton} ${styles.rightScrollButton}`}>
|
||||
<Button onClick={doScroll(true)} className={`${styles.scrollButton} ${styles.rightScrollButton}`}>
|
||||
<img src={rightAngleIcon} alt='Показать ещё' />
|
||||
</button>
|
||||
</Button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
@ -1,36 +0,0 @@
|
||||
import { Marker, Tooltip, useMap } from 'react-leaflet'
|
||||
import { LatLng } from 'leaflet'
|
||||
|
||||
import { locationsIcons, studentLocations } from '../assets/studentLocations'
|
||||
|
||||
type StudentLocationsProps = {
|
||||
setPosition?: (pos: LatLng) => void
|
||||
}
|
||||
|
||||
function StudentLocations({ setPosition }: StudentLocationsProps) {
|
||||
const map = useMap()
|
||||
|
||||
return (
|
||||
<>{
|
||||
studentLocations.map((el) =>
|
||||
<Marker
|
||||
icon={locationsIcons[el.type]}
|
||||
position={el.position}
|
||||
eventHandlers={{
|
||||
click: setPosition && (() => {
|
||||
setPosition(new LatLng(...el.position))
|
||||
map.setView(el.position)
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<Tooltip>
|
||||
{el.name}
|
||||
</Tooltip>
|
||||
</Marker>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default StudentLocations
|
@ -17,4 +17,3 @@ export { default as SelectDisposalTrashbox } from './SelectDisposalTrashbox'
|
||||
export { default as StarRating } from './StarRating'
|
||||
export { default as CardLayout } from './CardLayout'
|
||||
export { default as LocateButton } from './LocateButton'
|
||||
export { default as StudentLocations } from './StudentLocations'
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { CSSProperties, FormEventHandler, useEffect, useMemo, useState } from 'react'
|
||||
import { CSSProperties, FormEventHandler, useEffect, useState } from 'react'
|
||||
import { Form, Button } from 'react-bootstrap'
|
||||
import { MapContainer, TileLayer } from 'react-leaflet'
|
||||
import { latLng } from 'leaflet'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
import { MapClickHandler, LocationMarker, CardLayout, LocateButton, StudentLocations } from '../components'
|
||||
import { MapClickHandler, LocationMarker, CardLayout, LocateButton } from '../components'
|
||||
import { useAddAnnouncement } from '../hooks/api'
|
||||
import { categories, categoryNames } from '../assets/category'
|
||||
import { stations, lines, lineNames, DEFAULT_LINE } from '../assets/metro'
|
||||
import { stations, lines, lineNames } from '../assets/metro'
|
||||
import { fallbackError, gotResponse } from '../hooks/useFetch'
|
||||
import { useOsmAddresses } from '../hooks/api'
|
||||
|
||||
@ -38,7 +38,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 handleAdd(formData)
|
||||
handleAdd(formData)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@ -51,15 +51,15 @@ function AddPage() {
|
||||
<CardLayout text='Опубликовать объявление'>
|
||||
<Form onSubmit={handleSubmit}>
|
||||
<Form.Group className='mb-3' controlId='name'>
|
||||
<Form.Label>Как ошляпите объявление?</Form.Label>
|
||||
<Form.Label>Заголовок объявления</Form.Label>
|
||||
<Form.Control type='text' required name='name' />
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className='mb-3' controlId='category'>
|
||||
<Form.Label>Какая категория?</Form.Label>
|
||||
<Form.Label>Категория</Form.Label>
|
||||
<Form.Select required name='category'>
|
||||
<option value='' hidden>
|
||||
Выберите категорию предмета
|
||||
Выберите категорию
|
||||
</option>
|
||||
{categories.map(category =>
|
||||
<option key={category} value={category}>{categoryNames[category]}</option>
|
||||
@ -68,12 +68,12 @@ function AddPage() {
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className='mb-3' controlId='bestBy'>
|
||||
<Form.Label>Когда закрыть объявление?</Form.Label>
|
||||
<Form.Label>Срок годности</Form.Label>
|
||||
<Form.Control type='date' required name='bestBy' />
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className='mb-3' controlId='address'>
|
||||
<Form.Label>Где забирать?</Form.Label>
|
||||
<Form.Label>Адрес выдачи</Form.Label>
|
||||
<div className='mb-3'>
|
||||
<MapContainer
|
||||
scrollWheelZoom={false}
|
||||
@ -86,8 +86,6 @@ function AddPage() {
|
||||
url='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
|
||||
/>
|
||||
|
||||
{useMemo(() => <StudentLocations setPosition={setAddressPosition} />, [])}
|
||||
|
||||
<LocationMarker
|
||||
address={gotResponse(address) ? fallbackError(address) : 'Загрузка...'}
|
||||
position={addressPosition}
|
||||
@ -105,7 +103,7 @@ function AddPage() {
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className='mb-3' controlId='description'>
|
||||
<Form.Label>Пару слов о вас и предмете?</Form.Label>
|
||||
<Form.Label>Описание</Form.Label>
|
||||
<Form.Control
|
||||
as='textarea'
|
||||
name='description'
|
||||
@ -115,7 +113,7 @@ function AddPage() {
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className='mb-3' controlId='src'>
|
||||
<Form.Label>Загрузите гляделки? (фото или видео)</Form.Label>
|
||||
<Form.Label>Иллюстрация (фото или видео)</Form.Label>
|
||||
<Form.Control
|
||||
type='file'
|
||||
name='src'
|
||||
@ -126,9 +124,12 @@ function AddPage() {
|
||||
|
||||
<Form.Group className='mb-3' controlId='metro'>
|
||||
<Form.Label>
|
||||
Где метро поближе?
|
||||
Станция метро
|
||||
</Form.Label>
|
||||
<Form.Select name='metro' defaultValue={DEFAULT_LINE}>
|
||||
<Form.Select name='metro'>
|
||||
<option value=''>
|
||||
Укажите ближайщую станцию метро
|
||||
</option>
|
||||
{lines.map(
|
||||
line =>
|
||||
<optgroup key={line} label={lineNames[line]}>
|
||||
|
@ -48,11 +48,6 @@ function UserPage() {
|
||||
<div className='mb-3'>
|
||||
<Poetry />
|
||||
</div>
|
||||
|
||||
<div className='mb-3'>
|
||||
<h4>Связаться с разработчиками</h4>
|
||||
Канал в телеграме: <a href='https://t.me/porridger'>Porridger</a>
|
||||
</div>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
@ -5,8 +5,3 @@
|
||||
.text {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 100%;
|
||||
border-radius: 12px;
|
||||
}
|
@ -42,7 +42,6 @@
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
background: linear-gradient(to right, rgba(17, 17, 17, 0) 0%, rgba(17, 17, 17, 255) 100%);
|
||||
background-color: transparent;
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 10%;
|
||||
|
@ -2,10 +2,6 @@ import L from 'leaflet'
|
||||
|
||||
import itemMarker from '../assets/itemMarker.png'
|
||||
import trashMarker from '../assets/trashMarker.png'
|
||||
import dormitoryMarker from '../assets/dormitoryMarker.png'
|
||||
import letiMarker from '../assets/letiMarker.png'
|
||||
import itmoMarker from '../assets/itmoMarker.png'
|
||||
|
||||
|
||||
const iconItem = new L.Icon({
|
||||
iconUrl: itemMarker,
|
||||
@ -21,25 +17,4 @@ const iconTrash = new L.Icon({
|
||||
iconSize: [34, 41],
|
||||
})
|
||||
|
||||
const iconDormitory = new L.Icon({
|
||||
iconUrl: dormitoryMarker,
|
||||
iconRetinaUrl: dormitoryMarker,
|
||||
popupAnchor: [0, 0],
|
||||
iconSize: [41, 41],
|
||||
})
|
||||
|
||||
const iconLETI = new L.Icon({
|
||||
iconUrl: letiMarker,
|
||||
iconRetinaUrl: letiMarker,
|
||||
popupAnchor: [0, 0],
|
||||
iconSize: [41, 41],
|
||||
})
|
||||
|
||||
const iconITMO = new L.Icon({
|
||||
iconUrl: itmoMarker,
|
||||
iconRetinaUrl: itmoMarker,
|
||||
popupAnchor: [0, 0],
|
||||
iconSize: [41, 41],
|
||||
})
|
||||
|
||||
export { iconItem, iconTrash, iconDormitory, iconLETI, iconITMO }
|
||||
export { iconItem, iconTrash }
|
||||
|
@ -1,8 +1,8 @@
|
||||
"""lazy=selectin added to user table relationships
|
||||
"""create constructor to make object (if leave as is, with two primary keys, says that one argument required, but two were given). Booked_counter can be nullable
|
||||
|
||||
Revision ID: 8e631a2fe6b8
|
||||
Revises:
|
||||
Create Date: 2023-09-02 23:45:08.799366
|
||||
Revision ID: 19dbd9793f11
|
||||
Revises: 945c70aa70e7
|
||||
Create Date: 2024-08-13 15:29:21.542539
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
@ -12,8 +12,8 @@ import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '8e631a2fe6b8'
|
||||
down_revision: Union[str, None] = None
|
||||
revision: str = '19dbd9793f11'
|
||||
down_revision: Union[str, None] = '945c70aa70e7'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
@ -20,7 +20,6 @@ def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('announcements', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('user_id', sa.Integer(), nullable=True))
|
||||
batch_op.add_column(sa.Column('state', sa.Enum('published', 'taken', 'obsolete', name='state'), nullable=True))
|
||||
# batch_op.drop_constraint(None, type_='foreignkey')
|
||||
batch_op.create_foreign_key('fk_users_id', 'users', ['user_id'], ['id'])
|
||||
batch_op.drop_column('owner_id')
|
||||
@ -42,7 +41,6 @@ def downgrade():
|
||||
batch_op.add_column(sa.Column('owner_id', sa.INTEGER(), nullable=True))
|
||||
# batch_op.drop_constraint('fk_users_id', type_='foreignkey')
|
||||
batch_op.create_foreign_key(None, 'users', ['owner_id'], ['id'])
|
||||
batch_op.drop_column('state')
|
||||
batch_op.drop_column('user_id')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
@ -18,13 +18,6 @@ depends_on = None
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('poems', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('title', sa.String(), nullable=True))
|
||||
batch_op.add_column(sa.Column('text', sa.String(), nullable=True))
|
||||
batch_op.add_column(sa.Column('author', sa.String(), nullable=True))
|
||||
batch_op.drop_column('poem_name')
|
||||
batch_op.drop_column('poem_text')
|
||||
|
||||
with op.batch_alter_table('trashboxes', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('category', sa.String(), nullable=True))
|
||||
batch_op.drop_column('categories')
|
||||
@ -37,12 +30,4 @@ def downgrade():
|
||||
with op.batch_alter_table('trashboxes', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('categories', sa.VARCHAR(), nullable=True))
|
||||
batch_op.drop_column('category')
|
||||
|
||||
with op.batch_alter_table('poems', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('poem_text', sa.VARCHAR(), nullable=True))
|
||||
batch_op.add_column(sa.Column('poem_name', sa.VARCHAR(), nullable=True))
|
||||
batch_op.drop_column('author')
|
||||
batch_op.drop_column('text')
|
||||
batch_op.drop_column('title')
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
@ -0,0 +1,66 @@
|
||||
"""Added booked_by column which contains id of users who booked the announcement old booked_by renamed to booked_counter
|
||||
|
||||
Revision ID: 5a8105ac1a4f
|
||||
Revises: 547f860f21a7
|
||||
Create Date: 2024-08-09 15:07:51.386406
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '5a8105ac1a4f'
|
||||
down_revision: Union[str, None] = '547f860f21a7'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('announcements', sa.Column('booked_counter', sa.Integer(), nullable=True))
|
||||
op.alter_column('announcements', 'longtitude',
|
||||
existing_type=sa.INTEGER(),
|
||||
type_=sa.Float(),
|
||||
existing_nullable=True)
|
||||
op.alter_column('announcements', 'latitude',
|
||||
existing_type=sa.INTEGER(),
|
||||
type_=sa.Float(),
|
||||
existing_nullable=True)
|
||||
op.drop_column('announcements', 'booked_by')
|
||||
op.alter_column('trashboxes', 'latitude',
|
||||
existing_type=sa.INTEGER(),
|
||||
type_=sa.Float(),
|
||||
existing_nullable=True)
|
||||
op.alter_column('trashboxes', 'longtitude',
|
||||
existing_type=sa.INTEGER(),
|
||||
type_=sa.Float(),
|
||||
existing_nullable=True)
|
||||
op.create_foreign_key(None, 'trashboxes', 'users', ['user_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'trashboxes', type_='foreignkey')
|
||||
op.alter_column('trashboxes', 'longtitude',
|
||||
existing_type=sa.Float(),
|
||||
type_=sa.INTEGER(),
|
||||
existing_nullable=True)
|
||||
op.alter_column('trashboxes', 'latitude',
|
||||
existing_type=sa.Float(),
|
||||
type_=sa.INTEGER(),
|
||||
existing_nullable=True)
|
||||
op.add_column('announcements', sa.Column('booked_by', sa.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.alter_column('announcements', 'latitude',
|
||||
existing_type=sa.Float(),
|
||||
type_=sa.INTEGER(),
|
||||
existing_nullable=True)
|
||||
op.alter_column('announcements', 'longtitude',
|
||||
existing_type=sa.Float(),
|
||||
type_=sa.INTEGER(),
|
||||
existing_nullable=True)
|
||||
op.drop_column('announcements', 'booked_counter')
|
||||
# ### end Alembic commands ###
|
@ -0,0 +1,38 @@
|
||||
"""booked_by colomn changed to Integer, added new table AnnouncementUser implementing one to many relationships
|
||||
|
||||
Revision ID: 945c70aa70e7
|
||||
Revises: 5a8105ac1a4f
|
||||
Create Date: 2024-08-12 20:36:08.669574
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '945c70aa70e7'
|
||||
down_revision: Union[str, None] = '5a8105ac1a4f'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('announcementuser',
|
||||
sa.Column('announcement_id', sa.Integer(), nullable=False),
|
||||
sa.Column('booking_user_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['announcement_id'], ['announcements.id'], ),
|
||||
sa.ForeignKeyConstraint(['booking_user_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('announcement_id', 'booking_user_id')
|
||||
)
|
||||
op.add_column('announcements', sa.Column('booked_by', sa.Integer(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('announcements', 'booked_by')
|
||||
op.drop_table('announcementuser')
|
||||
# ### end Alembic commands ###
|
@ -18,10 +18,8 @@ depends_on = None
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('_alembic_tmp_users')
|
||||
with op.batch_alter_table('announcements', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('obsolete', sa.Boolean(), nullable=True))
|
||||
batch_op.drop_column('state')
|
||||
|
||||
with op.batch_alter_table('poems', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('title', sa.String(), nullable=True))
|
||||
@ -49,16 +47,5 @@ def downgrade():
|
||||
batch_op.drop_column('title')
|
||||
|
||||
with op.batch_alter_table('announcements', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('state', sa.VARCHAR(length=9), nullable=True))
|
||||
batch_op.drop_column('obsolete')
|
||||
|
||||
op.create_table('_alembic_tmp_users',
|
||||
sa.Column('id', sa.INTEGER(), nullable=False),
|
||||
sa.Column('email', sa.VARCHAR(), nullable=True),
|
||||
sa.Column('hashed_password', sa.VARCHAR(), nullable=True),
|
||||
sa.Column('name', sa.VARCHAR(), nullable=True),
|
||||
sa.Column('surname', sa.VARCHAR(), nullable=True),
|
||||
sa.Column('disabled', sa.BOOLEAN(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
Reference in New Issue
Block a user