json
This commit is contained in:
		
							parent
							
								
									ab4454320a
								
							
						
					
					
						commit
						a3723bcdd6
					
				
							
								
								
									
										18519
									
								
								logs/app.log
								
								
								
								
							
							
						
						
									
										18519
									
								
								logs/app.log
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										13
									
								
								main.py
								
								
								
								
							
							
						
						
									
										13
									
								
								main.py
								
								
								
								
							| 
						 | 
					@ -3,7 +3,8 @@ from fastapi.responses import RedirectResponse
 | 
				
			||||||
from fastapi.templating import Jinja2Templates
 | 
					from fastapi.templating import Jinja2Templates
 | 
				
			||||||
from fastapi.staticfiles import StaticFiles
 | 
					from fastapi.staticfiles import StaticFiles
 | 
				
			||||||
from model.database import init_db
 | 
					from model.database import init_db
 | 
				
			||||||
from routers import index, logins, users, product, profile, jobs, client
 | 
					from routers import index, logins, users, product, profile, jobs, client, auth1
 | 
				
			||||||
 | 
					from fastapi.middleware.cors import CORSMiddleware
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,6 +24,14 @@ app = FastAPI(title="API для turboapply",
 | 
				
			||||||
            #   on_startup=[start_workers]
 | 
					            #   on_startup=[start_workers]
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app.add_middleware(
 | 
				
			||||||
 | 
					    CORSMiddleware,
 | 
				
			||||||
 | 
					    allow_origins=["*"],  # Разрешить запросы отовсюду (для тестов)
 | 
				
			||||||
 | 
					    allow_credentials=True,
 | 
				
			||||||
 | 
					    allow_methods=["*"],
 | 
				
			||||||
 | 
					    allow_headers=["*"],
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Настройка шаблонов
 | 
					# Настройка шаблонов
 | 
				
			||||||
templates = Jinja2Templates(directory="templates")
 | 
					templates = Jinja2Templates(directory="templates")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,6 +44,8 @@ app.include_router(logins.router, tags=["login"], include_in_schema=False)
 | 
				
			||||||
app.include_router(users.router, tags=["users"], include_in_schema=False)
 | 
					app.include_router(users.router, tags=["users"], include_in_schema=False)
 | 
				
			||||||
app.include_router(product.router, tags=["product"], include_in_schema=False)
 | 
					app.include_router(product.router, tags=["product"], include_in_schema=False)
 | 
				
			||||||
app.include_router(client.router, tags=["client"])
 | 
					app.include_router(client.router, tags=["client"])
 | 
				
			||||||
 | 
					app.include_router(auth1.router, tags=["auth1"], include_in_schema=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Подключение роутеров
 | 
					# Подключение роутеров
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,8 @@
 | 
				
			||||||
from sqlalchemy import Column, Integer, String, Text, DateTime, Float, Boolean, BigInteger, ForeignKey, DateTime
 | 
					from sqlalchemy import Column, Integer, String, Text, DateTime, Float, Boolean, BigInteger, ForeignKey, DateTime
 | 
				
			||||||
from sqlalchemy.dialects.mysql import JSON
 | 
					from sqlalchemy.dialects.mysql import JSON
 | 
				
			||||||
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
 | 
					from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
 | 
				
			||||||
 | 
					from typing import AsyncGenerator
 | 
				
			||||||
 | 
					from sqlalchemy.ext.asyncio import AsyncSession
 | 
				
			||||||
from sqlalchemy.orm import relationship
 | 
					from sqlalchemy.orm import relationship
 | 
				
			||||||
from sqlalchemy.sql import func
 | 
					from sqlalchemy.sql import func
 | 
				
			||||||
from sqlalchemy.orm import sessionmaker, declarative_base
 | 
					from sqlalchemy.orm import sessionmaker, declarative_base
 | 
				
			||||||
| 
						 | 
					@ -50,9 +51,14 @@ async def init_db():
 | 
				
			||||||
            await session.commit()
 | 
					            await session.commit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Зависимость FastAPI для работы с БД
 | 
					# Зависимость FastAPI для работы с БД
 | 
				
			||||||
async def get_async_session() -> AsyncSession:
 | 
					# async def get_async_session() -> AsyncSession:
 | 
				
			||||||
 | 
					#     async with async_session() as session:
 | 
				
			||||||
 | 
					#         yield session
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
 | 
				
			||||||
    async with async_session() as session:
 | 
					    async with async_session() as session:
 | 
				
			||||||
        yield session
 | 
					        yield session  # Сессия остается открытой во время запроса        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Функции для хеширования паролей
 | 
					# Функции для хеширования паролей
 | 
				
			||||||
def hash_password(password: str) -> str:
 | 
					def hash_password(password: str) -> str:
 | 
				
			||||||
| 
						 | 
					@ -115,6 +121,7 @@ class Job(Base):
 | 
				
			||||||
    link = Column(String(2083), nullable=True)  # URL вакансии
 | 
					    link = Column(String(2083), nullable=True)  # URL вакансии
 | 
				
			||||||
    link_company = Column(String(2083), nullable=True)  # URL компании
 | 
					    link_company = Column(String(2083), nullable=True)  # URL компании
 | 
				
			||||||
    active = Column(Boolean, default=True)  # Вакансия активна?
 | 
					    active = Column(Boolean, default=True)  # Вакансия активна?
 | 
				
			||||||
 | 
					    text = Column(Text, nullable=True)
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    # Связь с AppliedJobs
 | 
					    # Связь с AppliedJobs
 | 
				
			||||||
| 
						 | 
					@ -129,6 +136,9 @@ class Client(Base):
 | 
				
			||||||
    user_login = Column(String(55), unique=True, index=True, nullable=False)  # ✅ Правильно
 | 
					    user_login = Column(String(55), unique=True, index=True, nullable=False)  # ✅ Правильно
 | 
				
			||||||
    user_nicename = Column(String(55), unique=True, index=True, nullable=True)  # ✅ Правильно
 | 
					    user_nicename = Column(String(55), unique=True, index=True, nullable=True)  # ✅ Правильно
 | 
				
			||||||
    user_email = Column(String(55), unique=True, index=True, nullable=True)  # ✅ Правильно
 | 
					    user_email = Column(String(55), unique=True, index=True, nullable=True)  # ✅ Правильно
 | 
				
			||||||
 | 
					    phone = Column(String(55), unique=True, index=True, nullable=True)  # ✅ Правильно
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    json_data = Column(Text, nullable=True)
 | 
				
			||||||
    # user_registered = Column(String(2083), nullable=True)  # URL вакансии
 | 
					    # user_registered = Column(String(2083), nullable=True)  # URL вакансии
 | 
				
			||||||
    user_status = Column(Boolean, default=True)
 | 
					    user_status = Column(Boolean, default=True)
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,68 @@
 | 
				
			||||||
 | 
					from fastapi import FastAPI, Depends, HTTPException, status, Form, Response, Request, APIRouter
 | 
				
			||||||
 | 
					from sqlalchemy.ext.asyncio import AsyncSession
 | 
				
			||||||
 | 
					from sqlalchemy.future import select
 | 
				
			||||||
 | 
					from passlib.context import CryptContext
 | 
				
			||||||
 | 
					import jwt
 | 
				
			||||||
 | 
					from datetime import datetime, timedelta
 | 
				
			||||||
 | 
					from model.database import get_async_session, User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SECRET_KEY = "your_secret_key"
 | 
				
			||||||
 | 
					REFRESH_SECRET_KEY = "your_refresh_secret_key"
 | 
				
			||||||
 | 
					ALGORITHM = "HS256"
 | 
				
			||||||
 | 
					ACCESS_TOKEN_EXPIRE_MINUTES = 1
 | 
				
			||||||
 | 
					pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def create_access_token(data: dict, expires_delta: timedelta | None = None):
 | 
				
			||||||
 | 
					    to_encode = data.copy()
 | 
				
			||||||
 | 
					    expire = datetime.utcnow() + (expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
 | 
				
			||||||
 | 
					    to_encode.update({"exp": expire})
 | 
				
			||||||
 | 
					    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def authenticate_user(db: AsyncSession, username: str, password: str):
 | 
				
			||||||
 | 
					    result = await db.execute(select(User).where(User.username == username))
 | 
				
			||||||
 | 
					    user = result.scalars().first()
 | 
				
			||||||
 | 
					    return user if user and pwd_context.verify(password, user.hashed_password) else None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					router = APIRouter(prefix="/auth", tags=["auth"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.post("/logins")
 | 
				
			||||||
 | 
					async def logins(username: str = Form(...), password: str = Form(...), db: AsyncSession = Depends(get_async_session)):
 | 
				
			||||||
 | 
					    user = await authenticate_user(db, username, password)
 | 
				
			||||||
 | 
					    if not user:
 | 
				
			||||||
 | 
					        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Неправильный логин или пароль")
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    access_token = create_access_token({"sub": user.username, "role": user.role})
 | 
				
			||||||
 | 
					    refresh_token = create_access_token({"sub": user.username}, expires_delta=timedelta(days=7))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    response = Response()
 | 
				
			||||||
 | 
					    response.set_cookie(key="access_token", value=access_token, httponly=True)
 | 
				
			||||||
 | 
					    response.set_cookie(key="refresh_token", value=refresh_token, httponly=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {"message": "Успешный вход"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.post("/logout")
 | 
				
			||||||
 | 
					async def logout(response: Response):
 | 
				
			||||||
 | 
					    response.delete_cookie("access_token")
 | 
				
			||||||
 | 
					    response.delete_cookie("refresh_token")
 | 
				
			||||||
 | 
					    return {"message": "Вы вышли"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@router.post("/refresh")
 | 
				
			||||||
 | 
					async def refresh_token(request: Request, response: Response):
 | 
				
			||||||
 | 
					    refresh_token = request.cookies.get("refresh_token")
 | 
				
			||||||
 | 
					    if not refresh_token:
 | 
				
			||||||
 | 
					        raise HTTPException(status_code=401, detail="❌ Refresh token отсутствует")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        payload = jwt.decode(refresh_token, REFRESH_SECRET_KEY, algorithms=[ALGORITHM])
 | 
				
			||||||
 | 
					        username = payload.get("sub")
 | 
				
			||||||
 | 
					        if not username:
 | 
				
			||||||
 | 
					            raise HTTPException(status_code=401, detail="❌ Ошибка токена")
 | 
				
			||||||
 | 
					    except jwt.ExpiredSignatureError:
 | 
				
			||||||
 | 
					        raise HTTPException(status_code=401, detail="❌ Refresh token истёк")
 | 
				
			||||||
 | 
					    except jwt.InvalidTokenError:
 | 
				
			||||||
 | 
					        raise HTTPException(status_code=401, detail="❌ Ошибка refresh токена")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    new_access_token = create_access_token({"sub": username})
 | 
				
			||||||
 | 
					    response.set_cookie(key="access_token", value=new_access_token, httponly=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {"access_token": new_access_token}
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,13 @@
 | 
				
			||||||
from fastapi import FastAPI, HTTPException, APIRouter, Request, Header
 | 
					from fastapi import FastAPI, HTTPException, APIRouter, Request, Header, Depends
 | 
				
			||||||
from fastapi.responses import JSONResponse
 | 
					from fastapi.responses import JSONResponse
 | 
				
			||||||
from fastapi.templating import Jinja2Templates
 | 
					from fastapi.templating import Jinja2Templates
 | 
				
			||||||
from typing import Dict
 | 
					from typing import Dict
 | 
				
			||||||
from pydantic import BaseModel
 | 
					from pydantic import BaseModel
 | 
				
			||||||
 | 
					from sqlalchemy.orm import Session
 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
 | 
					from model.database import get_async_session, Client
 | 
				
			||||||
 | 
					from utils.clients import upsert_client
 | 
				
			||||||
 | 
					from typing import Union
 | 
				
			||||||
 | 
					
 | 
				
			||||||
router = APIRouter()
 | 
					router = APIRouter()
 | 
				
			||||||
templates = Jinja2Templates(directory="templates")
 | 
					templates = Jinja2Templates(directory="templates")
 | 
				
			||||||
| 
						 | 
					@ -11,7 +15,7 @@ API_KEY = "4545454"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Пример данных
 | 
					# Пример данных
 | 
				
			||||||
clients = {
 | 
					clients = {
 | 
				
			||||||
    27: {"username": "John Doe", "email": "john@example.com", "phone": "+123456781"},
 | 
					    1: {"username": "John Doe", "email": "john@example.com", "phone": "+123456781"},
 | 
				
			||||||
    44: {"username": "Jane Smith", "email": "jane@example.com", "phone": "+987654321"}
 | 
					    44: {"username": "Jane Smith", "email": "jane@example.com", "phone": "+987654321"}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@router.get("/get_client/{client_id}", include_in_schema=False)
 | 
					@router.get("/get_client/{client_id}", include_in_schema=False)
 | 
				
			||||||
| 
						 | 
					@ -22,17 +26,36 @@ async def get_client_modal(client_id: int):
 | 
				
			||||||
    return JSONResponse(content=client)
 | 
					    return JSONResponse(content=client)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class JsonData(BaseModel):
 | 
					class JsonData(BaseModel):
 | 
				
			||||||
    json_data: str
 | 
					    json_data: Union[dict, str]
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
@router.post("/client/")
 | 
					@router.post("/client/")
 | 
				
			||||||
async def client(data: JsonData, x_api_key: str = Header(None)):
 | 
					async def client(data: JsonData, x_api_key: str = Header(...), db: Session = Depends(get_async_session)):
 | 
				
			||||||
    # Проверяем API-ключ
 | 
					 | 
				
			||||||
    if x_api_key != "4545454":
 | 
					    if x_api_key != "4545454":
 | 
				
			||||||
        raise HTTPException(status_code=403, detail="Invalid API Key")
 | 
					        raise HTTPException(status_code=403, detail="Invalid API Key")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Если json_data строка — декодируем
 | 
				
			||||||
 | 
					    if isinstance(data.json_data, str):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            data.json_data = json.loads(data.json_data)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        except json.JSONDecodeError:
 | 
				
			||||||
 | 
					            raise HTTPException(status_code=400, detail="Invalid JSON format")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print("Полученные данные:", data.json_data)
 | 
				
			||||||
 | 
					    return {"message": "JSON получен", "data": "ok"}
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    try:
 | 
					    
 | 
				
			||||||
        parsed_data = json.loads(data.json_data)  # Декодируем JSON
 | 
					    
 | 
				
			||||||
        print("Полученные данные:", parsed_data)
 | 
					    
 | 
				
			||||||
        return {"message": "JSON получен", "data": parsed_data}
 | 
					          # first_name = parsed_data['first_name']
 | 
				
			||||||
    except json.JSONDecodeError as e:
 | 
					        # last_name = parsed_data['last_name']
 | 
				
			||||||
        raise HTTPException(status_code=400, detail=f"Invalid JSON: {str(e)}")
 | 
					        # email_addr = parsed_data['email_addr']
 | 
				
			||||||
 | 
					        # user_id = parsed_data['user_id']
 | 
				
			||||||
 | 
					        # phone_num = parsed_data['phone_num']
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # print(first_name)
 | 
				
			||||||
 | 
					        # # data = await upsert_client(db, first_name, last_name, email_addr, parsed_data) 
 | 
				
			||||||
 | 
					        # client = await upsert_client(db, user_id, first_name, last_name, email_addr, phone_num, str(parsed_data))
 | 
				
			||||||
 | 
					        # print(f"Received data: data={client}")
 | 
				
			||||||
| 
						 | 
					@ -52,7 +52,7 @@ async def login(
 | 
				
			||||||
        return Response(content="Неправильный логин или пароль", media_type="text/html")
 | 
					        return Response(content="Неправильный логин или пароль", media_type="text/html")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    access_token = create_access_token(data={"sub": username, "role": user.role})
 | 
					    access_token = create_access_token(data={"sub": username, "role": user.role})
 | 
				
			||||||
    response = RedirectResponse(url="/", status_code=303)
 | 
					    response = RedirectResponse(url="/product", status_code=303)
 | 
				
			||||||
    response.set_cookie(key="access_token", value=access_token, httponly=True)
 | 
					    response.set_cookie(key="access_token", value=access_token, httponly=True)
 | 
				
			||||||
    return response
 | 
					    return response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										4780
									
								
								search_jobes2.json
								
								
								
								
							
							
						
						
									
										4780
									
								
								search_jobes2.json
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -28,12 +28,7 @@
 | 
				
			||||||
                </a>
 | 
					                </a>
 | 
				
			||||||
                <div class="side-nav__devider my-6"></div>
 | 
					                <div class="side-nav__devider my-6"></div>
 | 
				
			||||||
                <ul>
 | 
					                <ul>
 | 
				
			||||||
                    <li>
 | 
					                    
 | 
				
			||||||
                        <a href="/" class="side-menu {% if current_path == '/' %}side-menu--active{% endif %}">
 | 
					 | 
				
			||||||
                            <div class="side-menu__icon"> <i data-feather="home"></i> </div>
 | 
					 | 
				
			||||||
                            <div class="side-menu__title"> Dashboard </div>
 | 
					 | 
				
			||||||
                        </a>
 | 
					 | 
				
			||||||
                    </li>
 | 
					 | 
				
			||||||
                    <li>
 | 
					                    <li>
 | 
				
			||||||
                        <a href="/product" class="side-menu {% if current_path.startswith('/product') %}side-menu--active{% endif %}">
 | 
					                        <a href="/product" class="side-menu {% if current_path.startswith('/product') %}side-menu--active{% endif %}">
 | 
				
			||||||
                            <div class="side-menu__icon"> <i data-feather="credit-card"></i> </div>
 | 
					                            <div class="side-menu__icon"> <i data-feather="credit-card"></i> </div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,7 +31,7 @@
 | 
				
			||||||
<h2 class="text-lg font-medium mr-auto">Add Users</h2>
 | 
					<h2 class="text-lg font-medium mr-auto">Add Users</h2>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<form action="/users/create" method="post">
 | 
					<form action="/users/create" method="post">
 | 
				
			||||||
 <div class="mt-3"> <label>Email</label> <input type="text" class="input w-full border mt-2" name="username" placeholder="username"> </div>
 | 
					 <div class="mt-3"> <label>Username</label> <input type="text" class="input w-full border mt-2" name="username" placeholder="username"> </div>
 | 
				
			||||||
 <div class="mt-3"> <label>Password</label> <input type="password"   name="password"class="input w-full border mt-2" placeholder="secret"> </div>
 | 
					 <div class="mt-3"> <label>Password</label> <input type="password"   name="password"class="input w-full border mt-2" placeholder="secret"> </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 <label>Role:</label>
 | 
					 <label>Role:</label>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										179
									
								
								utils/app.py
								
								
								
								
							
							
						
						
									
										179
									
								
								utils/app.py
								
								
								
								
							| 
						 | 
					@ -47,10 +47,15 @@ def pars_jobs(geo):
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    for item in search_jobs
 | 
					    for item in search_jobs
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
    # print(search_job)
 | 
					    
 | 
				
			||||||
    file_path = "search_jobes2.json"
 | 
					    
 | 
				
			||||||
 | 
					    # file_path = "search_jobes2.json"
 | 
				
			||||||
 | 
					    # with open(file_path, "w", encoding="utf-8") as json_file:
 | 
				
			||||||
 | 
					    #     json.dump(search_jobes, json_file, indent=4, ensure_ascii=False)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    file_path = "search_jobes3.json"
 | 
				
			||||||
    with open(file_path, "w", encoding="utf-8") as json_file:
 | 
					    with open(file_path, "w", encoding="utf-8") as json_file:
 | 
				
			||||||
        json.dump(search_jobes, json_file, indent=4, ensure_ascii=False)
 | 
					        json.dump(search_jobs, json_file, indent=4, ensure_ascii=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    print(f"Результаты успешно сохранены в {file_path}")
 | 
					    print(f"Результаты успешно сохранены в {file_path}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,31 +64,90 @@ def add_to_bd():
 | 
				
			||||||
    #[ ]: Написать функцию записи в БД
 | 
					    #[ ]: Написать функцию записи в БД
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_job(job_id):
 | 
					async def get_job(db: AsyncSession, job_id: str):
 | 
				
			||||||
    jobs = api.get_job(job_id)
 | 
					    try:
 | 
				
			||||||
    text = jobs['description']['text']
 | 
					        jobs = api.get_job(job_id)
 | 
				
			||||||
    location = jobs['formattedLocation']
 | 
					        
 | 
				
			||||||
    title = jobs['title']
 | 
					        # Проверка наличия всех необходимых данных в ответе
 | 
				
			||||||
    listedAt = jobs['listedAt']
 | 
					        required_keys = ["description", "formattedLocation", "title", "listedAt", "companyDetails"]
 | 
				
			||||||
 | 
					        for key in required_keys:
 | 
				
			||||||
 | 
					            if key not in jobs:
 | 
				
			||||||
 | 
					                logging.error(f"❌ Ошибка: Ключ {key} отсутствует в API-ответе")
 | 
				
			||||||
 | 
					                return None
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Извлечение данных
 | 
				
			||||||
 | 
					        text = jobs['description']['text']
 | 
				
			||||||
 | 
					        location = jobs['formattedLocation']
 | 
				
			||||||
 | 
					        title = jobs['title']
 | 
				
			||||||
 | 
					        listed_at = jobs['listedAt']
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        company_info = jobs.get("companyDetails", {}).get("com.linkedin.voyager.deco.jobs.web.shared.WebCompactJobPostingCompany", {}).get("companyResolutionResult", {})
 | 
				
			||||||
 | 
					        company_name = company_info.get("name", "Unknown")
 | 
				
			||||||
 | 
					        company_url = company_info.get("url", "")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        link = f'https://www.linkedin.com/jobs/view/{job_id}/'
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        workplace_types = jobs.get("workplaceTypesResolutionResults", {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Преобразование из миллисекунд в секунды и конвертация
 | 
					        if workplace_types:
 | 
				
			||||||
    future_date = datetime.fromtimestamp(listedAt / 1000)
 | 
					            first_key = next(iter(workplace_types))  # Берём первый (и единственный) ключ
 | 
				
			||||||
    # Текущая дата
 | 
					            workplace_data = workplace_types[first_key]
 | 
				
			||||||
    current_date = datetime.now()
 | 
					            
 | 
				
			||||||
    # Разница в днях
 | 
					            localized_name = workplace_data.get("localizedName", "Unknown")
 | 
				
			||||||
    difference = abs((future_date - current_date).days)
 | 
					            entity_urn = workplace_data.get("entityUrn", "")
 | 
				
			||||||
    jobPostingId = jobs['jobPostingId']
 | 
					 | 
				
			||||||
    localizedName = jobs['workplaceTypesResolutionResults']
 | 
					 | 
				
			||||||
    # Извлекаем все localizedName
 | 
					 | 
				
			||||||
    localized_names = [value['localizedName'] for value in localizedName.values()]
 | 
					 | 
				
			||||||
    localized_name = ", ".join(localized_names)
 | 
					 | 
				
			||||||
    # localized_name = workplaceTypesResolutionResults['urn:li:fs_workplaceType:2']['localizedName']
 | 
					 | 
				
			||||||
    link = f'https://www.linkedin.com/jobs/view/{job_id}/'
 | 
					 | 
				
			||||||
    names = jobs['companyDetails']['com.linkedin.voyager.deco.jobs.web.shared.WebCompactJobPostingCompany']['companyResolutionResult']['name']
 | 
					 | 
				
			||||||
    url = jobs['companyDetails']['com.linkedin.voyager.deco.jobs.web.shared.WebCompactJobPostingCompany']['companyResolutionResult']['url']
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            logging.info(f"🏢 Тип работы: {localized_name} ({entity_urn})")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            localized_name = "Unknown"
 | 
				
			||||||
 | 
					            entity_urn = ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Теперь можно добавить эти значения в БД
 | 
				
			||||||
 | 
					        # job.location_type = localized_name
 | 
				
			||||||
 | 
					        # job.entity_urn = entity_urn
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # Проверка, есть ли вакансия в базе
 | 
				
			||||||
 | 
					        query = select(Job).filter(Job.job_id == job_id)
 | 
				
			||||||
 | 
					        result = await db.execute(query)
 | 
				
			||||||
 | 
					        job = result.scalars().first()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if job:
 | 
				
			||||||
 | 
					            logging.info(f"🔄 Обновление вакансии {job_id} в базе...")
 | 
				
			||||||
 | 
					            job.text = json.dumps(jobs)
 | 
				
			||||||
 | 
					            job.link = link
 | 
				
			||||||
 | 
					            job.location = location
 | 
				
			||||||
 | 
					            job.job_company = company_name
 | 
				
			||||||
 | 
					            job.link_company = company_url
 | 
				
			||||||
 | 
					            job.location_type = localized_name
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            logging.info(f"🆕 Добавление вакансии {job_id} в базу...")
 | 
				
			||||||
 | 
					            job = Job(
 | 
				
			||||||
 | 
					                job_id=job_id,
 | 
				
			||||||
 | 
					                text=json.dumps(jobs),
 | 
				
			||||||
 | 
					                link=link,
 | 
				
			||||||
 | 
					                location=location,
 | 
				
			||||||
 | 
					                job_company=company_name,
 | 
				
			||||||
 | 
					                link_company=company_url
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            db.add(job)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Коммит и обновление внутри активной сессии
 | 
				
			||||||
 | 
					        await db.commit()
 | 
				
			||||||
 | 
					        await db.refresh(job)
 | 
				
			||||||
 | 
					        logging.info(f"✅ Вакансия {job_id} успешно сохранена")
 | 
				
			||||||
 | 
					        return job
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    except Exception as e:
 | 
				
			||||||
 | 
					        # await db.rollback()  # Откат транзакции при ошибке
 | 
				
			||||||
 | 
					        logging.error(f"❌ Ошибка при обработке вакансии {job_id}: {e}")
 | 
				
			||||||
 | 
					        # return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # except Exception as e:
 | 
				
			||||||
 | 
					    #     await db.rollback()
 | 
				
			||||||
 | 
					    #     logging.error(f"❌ Ошибка при обработке вакансии {job_id}: {e}")
 | 
				
			||||||
 | 
					    #     return None
 | 
				
			||||||
    # [ ]: job_level job_type hourly_rate найти minimum_annual_salary и salary_currency добавил  description  names Компании url на компанию
 | 
					    # [ ]: job_level job_type hourly_rate найти minimum_annual_salary и salary_currency добавил  description  names Компании url на компанию
 | 
				
			||||||
    logging.info(f"title: {title},  location: {location},  jobPostingId: {jobPostingId}, difference: {difference}, location_type: {localized_name}, link: {link}  ===== {names} {url}") #text:{text},
 | 
					    # logging.info(f"title: {title},  location: {location},  jobPostingId: {jobPostingId}, difference: {difference}, location_type: {localized_name}, link: {link}  ===== {url}") #text:{text},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -132,41 +196,53 @@ async def get_vakansi():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
geo = '100025096'
 | 
					# geo = '100025096'
 | 
				
			||||||
# pars_jobs(geo)
 | 
					# pars_jobs(geo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# def main():
 | 
					# async def main():
 | 
				
			||||||
    # get_vakansi()
 | 
					#     # get_vakansi()
 | 
				
			||||||
    # logging.info("WORK")
 | 
					#     # logging.info("WORK")
 | 
				
			||||||
    # get_job('4130181356')
 | 
					#     # jobs = 
 | 
				
			||||||
 | 
					#     # async def main():
 | 
				
			||||||
 | 
					#     async for db in get_async_session():  # Асинхронный генератор сессий
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					#         query = select(Job).filter(Job.active == 2)
 | 
				
			||||||
 | 
					#         result = await db.execute(query)
 | 
				
			||||||
 | 
					#         job = result.scalars().first()
 | 
				
			||||||
 | 
					#         for j in job:
 | 
				
			||||||
 | 
					#             ids = j.job_id
 | 
				
			||||||
 | 
					#             print(ids)
 | 
				
			||||||
 | 
					#         # await get_job(db, '4192842821')
 | 
				
			||||||
 | 
					#         # break  # Выход после первого использования
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# pars_jobs(geo)
 | 
					# # pars_jobs(geo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# main()
 | 
					# if __name__ == "__main__":
 | 
				
			||||||
 | 
					#     asyncio.run(main())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from sqlalchemy.orm import sessionmaker, declarative_base
 | 
					# from sqlalchemy.orm import sessionmaker, declarative_base
 | 
				
			||||||
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
 | 
					# from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DATABASE_URL = os.getenv('DATABASE_URL') 
 | 
					# DATABASE_URL = os.getenv('DATABASE_URL') 
 | 
				
			||||||
engine = create_async_engine(DATABASE_URL, echo=True)
 | 
					# engine = create_async_engine(DATABASE_URL, echo=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
 | 
					# async_session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async_session_maker = sessionmaker(
 | 
					# async_session_maker = sessionmaker(
 | 
				
			||||||
    engine, class_=AsyncSession, expire_on_commit=False
 | 
					#     engine, class_=AsyncSession, expire_on_commit=False
 | 
				
			||||||
)
 | 
					# )
 | 
				
			||||||
async def main():
 | 
					# async def main():
 | 
				
			||||||
    await get_vakansi()
 | 
					#     await get_vakansi()
 | 
				
			||||||
    await engine.dispose()  # Корректно закрываем соединение перед завершением
 | 
					#     await engine.dispose()  # Корректно закрываем соединение перед завершением
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == "__main__":
 | 
					# if __name__ == "__main__":
 | 
				
			||||||
    asyncio.run(main())  # 🚀 Запускаем программу в единственном event loop
 | 
					#     asyncio.run(main())  # 🚀 Запускаем программу в единственном event loop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# [x] Обмен по времени не удалять main() что бы при старте сразу отрабатывала)
 | 
					# [x] Обмен по времени не удалять main() что бы при старте сразу отрабатывала)
 | 
				
			||||||
# Запуск функции каждые 5 минут
 | 
					# Запуск функции каждые 5 минут
 | 
				
			||||||
| 
						 | 
					@ -184,3 +260,16 @@ if __name__ == "__main__":
 | 
				
			||||||
#     time.sleep(1)  # Пауза между проверками
 | 
					#     time.sleep(1)  # Пауза между проверками
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def main():
 | 
				
			||||||
 | 
					    async for db in get_async_session():  # Асинхронный генератор сессий
 | 
				
			||||||
 | 
					        query = select(Job).filter(Job.active == 2)
 | 
				
			||||||
 | 
					        result = await db.execute(query)
 | 
				
			||||||
 | 
					        jobs = result.scalars().all()  # Получаем ВСЕ записи в виде списка
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for job in jobs:
 | 
				
			||||||
 | 
					            print(job.job_id)
 | 
				
			||||||
 | 
					            await get_job(db, job.job_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    asyncio.run(main())
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,43 @@
 | 
				
			||||||
 | 
					from sqlalchemy.ext.asyncio import AsyncSession
 | 
				
			||||||
 | 
					from sqlalchemy.future import select
 | 
				
			||||||
 | 
					from sqlalchemy.exc import IntegrityError
 | 
				
			||||||
 | 
					from model.database import Client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def upsert_client(db: AsyncSession, user_id: str, user_login: str,  user_nicename: str, user_email: str, phone: str, json_data: str):
 | 
				
			||||||
 | 
					    # Проверяем, существует ли клиент с таким логином или email
 | 
				
			||||||
 | 
					    async with db.begin():
 | 
				
			||||||
 | 
					        result = await db.execute(
 | 
				
			||||||
 | 
					            select(Client).filter((Client.id == user_id))# | (Client.user_email == user_email))
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        client = result.scalars().first()  # Получаем первый результат или None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if client:
 | 
				
			||||||
 | 
					        # Если клиент существует, обновляем его данные
 | 
				
			||||||
 | 
					        client.user_nicename = user_nicename
 | 
				
			||||||
 | 
					        client.phone = phone
 | 
				
			||||||
 | 
					        client.json_data = json_data
 | 
				
			||||||
 | 
					        # Можно добавить другие поля для обновления
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            await db.commit()  # Применяем изменения в базе данных
 | 
				
			||||||
 | 
					            await db.refresh(client)  # Обновляем объект в Python
 | 
				
			||||||
 | 
					            return client
 | 
				
			||||||
 | 
					        except IntegrityError:
 | 
				
			||||||
 | 
					            await db.rollback()  # В случае ошибки откатываем изменения
 | 
				
			||||||
 | 
					            raise
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        # Если клиента нет, создаем нового
 | 
				
			||||||
 | 
					        new_client = Client(
 | 
				
			||||||
 | 
					            user_login=user_login,
 | 
				
			||||||
 | 
					            user_nicename=user_nicename,
 | 
				
			||||||
 | 
					            user_email=user_email,
 | 
				
			||||||
 | 
					            json_data=json_data,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        db.add(new_client)
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            await db.commit()  # Сохраняем нового клиента в базе данных
 | 
				
			||||||
 | 
					            await db.refresh(new_client)  # Обновляем объект в Python
 | 
				
			||||||
 | 
					            return new_client
 | 
				
			||||||
 | 
					        except IntegrityError:
 | 
				
			||||||
 | 
					            await db.rollback()  # В случае ошибки откатываем изменения
 | 
				
			||||||
 | 
					            raise
 | 
				
			||||||
		Loading…
	
		Reference in New Issue