Add fields AppliedDate
This commit is contained in:
parent
6d56ad49e4
commit
c296ee8caa
17113
logs/app.log
17113
logs/app.log
File diff suppressed because it is too large
Load Diff
53766
logs/app.log.1
53766
logs/app.log.1
File diff suppressed because it is too large
Load Diff
50704
logs/app.log.2
50704
logs/app.log.2
File diff suppressed because it is too large
Load Diff
|
@ -20,6 +20,8 @@ from sqlalchemy import func
|
||||||
from routers.auth import get_current_user
|
from routers.auth import get_current_user
|
||||||
from model.database import get_async_session, Job, Client, AppliedJob, User
|
from model.database import get_async_session, Job, Client, AppliedJob, User
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
templates = Jinja2Templates(directory="templates")
|
templates = Jinja2Templates(directory="templates")
|
||||||
|
|
||||||
|
@ -394,10 +396,20 @@ async def update_status(data: StatusUpdate,
|
||||||
job = await session.execute(select(AppliedJob).where(AppliedJob.id == data.job_id))
|
job = await session.execute(select(AppliedJob).where(AppliedJob.id == data.job_id))
|
||||||
job = job.scalars().first()
|
job = job.scalars().first()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if not job:
|
if not job:
|
||||||
raise HTTPException(status_code=404, detail="Job not found")
|
raise HTTPException(status_code=404, detail="Job not found")
|
||||||
|
|
||||||
|
# job.status = data.status
|
||||||
|
# print(data.status)
|
||||||
|
# await session.commit()
|
||||||
job.status = data.status
|
job.status = data.status
|
||||||
|
|
||||||
|
# Устанавливаем дату, если статус становится "Applied"
|
||||||
|
if data.status.lower() == "applied" and job.applied_on is None:
|
||||||
|
job.applied_on = datetime.utcnow()
|
||||||
|
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
|
||||||
return {"message": "Статус обновлён", "job_id": job.id, "new_status": job.status}
|
return {"message": "Статус обновлён", "job_id": job.id, "new_status": job.status}
|
||||||
|
|
6902
search_jobes2.json
6902
search_jobes2.json
File diff suppressed because it is too large
Load Diff
|
@ -123,6 +123,7 @@
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<!-- <select data-placeholder="Select Status" class="select2 w-full" multiple name="status">-->
|
<!-- <select data-placeholder="Select Status" class="select2 w-full" multiple name="status">-->
|
||||||
<select data-placeholder="Select Status" class="select2 w-full" multiple name="status">
|
<select data-placeholder="Select Status" class="select2 w-full" multiple name="status">
|
||||||
|
<option value="Backlogged">Backlogged</option>
|
||||||
<option value="Scheduled">Scheduled</option>
|
<option value="Scheduled">Scheduled</option>
|
||||||
<option value="Requested">Requested</option>
|
<option value="Requested">Requested</option>
|
||||||
<option value="In-Progress">In-Progress</option>
|
<option value="In-Progress">In-Progress</option>
|
||||||
|
|
|
@ -40,84 +40,10 @@
|
||||||
{% include "filtr.html" %}
|
{% include "filtr.html" %}
|
||||||
|
|
||||||
|
|
||||||
|
{% include "tabl.html" %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- BEGIN: Datatable -->
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="intro-y datatable-wrapper box p-5 mt-5">
|
|
||||||
<table class="table table-report table-report--bordered display datatable w-full">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="border-b-2 whitespace-no-wrap">TITLE ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">COMPANY ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">CLIENT ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Requested on ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Posted on ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">STATUS</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Assignee</th>
|
|
||||||
|
|
||||||
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Applied on</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for job in jobs %}
|
|
||||||
<tr>
|
|
||||||
<td class="border-b">
|
|
||||||
<a href="{{ job.job.link }}" target="_blank" class="text-blue-500">{{ job.job.job_title [:50] }}</a>
|
|
||||||
<div class="text-gray-600 text-xs whitespace-no-wrap">{{ job.job.job_id }}</div>
|
|
||||||
</td>
|
|
||||||
<td class="text-center border-b">{{job.job.job_company}}</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
|
|
||||||
<a href="#" class="client-link text-blue-500" data-client-id="{{ job.client.id }}">{{ job.client.user_nicename }}</a>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td class="text-center border-b">{{job.job.data_requested.strftime('%Y-%m-%d')}}</td>
|
|
||||||
<td class="text-center border-b">{{job.job.date_posted.strftime('%Y-%m-%d')}}</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
<select class="select2" onchange="updateStatus(this, '{{ job.id }}')">
|
|
||||||
<option value="" {% if job.status is none %}selected{% endif %}>— Not selected —</option>
|
|
||||||
<option value="Scheduled" {% if job.status == "Scheduled" %}selected{% endif %}>Scheduled</option>
|
|
||||||
<option value="Requested" {% if job.status == "Requested" %}selected{% endif %}>Requested</option>
|
|
||||||
<option value="In-Progress" {% if job.status == "In-Progress" %}selected{% endif %}>In-Progress</option>
|
|
||||||
|
|
||||||
<option value="Paused" {% if job.status == "Paused" %}selected{% endif %} disabled>Paused</option>
|
|
||||||
|
|
||||||
<option value="Applied" {% if job.status == "Applied" %}selected{% endif %}>Applied</option>
|
|
||||||
<option value="Issues Applying" {% if job.status == "Issues Applying" %}selected{% endif %}>Issues Applying</option>
|
|
||||||
<option value="Cancelled" {% if job.status == "Cancelled" %}selected{% endif %}>Cancelled</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
<select class="select2" onchange="updateAssignee(this, '{{ job.id }}')">
|
|
||||||
<option value="" {% if job.assignee is none %}selected{% endif %}>— Not selected —</option>
|
|
||||||
{% for user in users %}
|
|
||||||
<option value="{{ user.id }}" {% if job.assignee == user.id %}selected{% endif %}>
|
|
||||||
{{ user.username }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
{% if job.applied_on %}
|
|
||||||
{{ job.applied_on.strftime('%Y-%m-%d') }}
|
|
||||||
{% else %}
|
|
||||||
-
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- END: Datatable -->
|
|
||||||
<!-- Включаем модалку -->
|
<!-- Включаем модалку -->
|
||||||
<!-- Модальное окно -->
|
<!-- Модальное окно -->
|
||||||
{% include "modal.html" %}
|
{% include "modal.html" %}
|
||||||
|
|
|
@ -185,7 +185,7 @@
|
||||||
<label>Status</label>
|
<label>Status</label>
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<select data-placeholder="Select Status" class="select2 w-full" multiple name="status">
|
<select data-placeholder="Select Status" class="select2 w-full" multiple name="status">
|
||||||
{% for option in ["Scheduled", "Requested", "In-Progress", "Paused", "Applied", "Issues Applying", "Cancelled"] %}
|
{% for option in ["Backlogged", "Scheduled", "Requested", "In-Progress", "Paused", "Applied", "Issues Applying", "Cancelled"] %}
|
||||||
<option value="{{ option }}" {% if option in status %}selected{% endif %}>{{ option }}</option>
|
<option value="{{ option }}" {% if option in status %}selected{% endif %}>{{ option }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
@ -230,75 +230,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- BEGIN: Datatable -->
|
{% include "tabl.html" %}
|
||||||
<div class="intro-y datatable-wrapper box p-5 mt-5">
|
|
||||||
<table class="table table-report table-report--bordered display datatable w-full">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="border-b-2 whitespace-no-wrap">TITLE ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">COMPANY ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">CLIENT ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Requested on ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Posted on ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">STATUS</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Assignee</th>
|
|
||||||
|
|
||||||
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Applied on</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for job in jobs %}
|
|
||||||
<tr>
|
|
||||||
<td class="border-b">
|
|
||||||
<a href="{{ job.job.link }}" target="_blank" class="text-blue-500">{{ job.job.job_title [:50] }}</a>
|
|
||||||
<div class="text-gray-600 text-xs whitespace-no-wrap">{{ job.job.job_id }}</div>
|
|
||||||
</td>
|
|
||||||
<td class="text-center border-b">{{job.job.job_company}}</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
|
|
||||||
<a href="#" class="client-link text-blue-500" data-client-id="{{ job.client.id }}">{{ job.client.user_nicename }}</a>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td class="text-center border-b">{{job.job.data_requested.strftime('%Y-%m-%d')}}</td>
|
|
||||||
<td class="text-center border-b">{{job.job.date_posted.strftime('%Y-%m-%d')}}</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
<select class="select2" onchange="updateStatus(this, '{{ job.id }}')">
|
|
||||||
<option value="" {% if job.status is none %}selected{% endif %}>— Not selected —</option>
|
|
||||||
<option value="Scheduled" {% if job.status == "Scheduled" %}selected{% endif %}>Scheduled</option>
|
|
||||||
<option value="Requested" {% if job.status == "Requested" %}selected{% endif %}>Requested</option>
|
|
||||||
<option value="In-Progress" {% if job.status == "In-Progress" %}selected{% endif %}>In-Progress</option>
|
|
||||||
<option value="Paused" {% if job.status == "Paused" %}selected{% endif %}>Paused</option>
|
|
||||||
<option value="Applied" {% if job.status == "Applied" %}selected{% endif %}>Applied</option>
|
|
||||||
<option value="Issues Applying" {% if job.status == "Issues Applying" %}selected{% endif %}>Issues Applying</option>
|
|
||||||
<option value="Cancelled" {% if job.status == "Cancelled" %}selected{% endif %}>Cancelled</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
<select class="select2" onchange="updateAssignee(this, '{{ job.id }}')">
|
|
||||||
<option value="" {% if job.assignee is none %}selected{% endif %}>— Not selected —</option>
|
|
||||||
{% for user in users %}
|
|
||||||
<option value="{{ user.id }}" {% if job.assignee == user.id %}selected{% endif %}>
|
|
||||||
{{ user.username }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
{% if job.applied_on %}
|
|
||||||
{{ job.applied_on.strftime('%Y-%m-%d') }}
|
|
||||||
{% else %}
|
|
||||||
-
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<!-- END: Datatable -->
|
|
||||||
<!-- Включаем модалку -->
|
<!-- Включаем модалку -->
|
||||||
<!-- Модальное окно -->
|
<!-- Модальное окно -->
|
||||||
{% include "modal.html" %}
|
{% include "modal.html" %}
|
||||||
|
|
|
@ -28,74 +28,7 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
{% include "filtr.html" %}
|
{% include "filtr.html" %}
|
||||||
<!-- BEGIN: Datatable -->
|
{% include "tabl.html" %}
|
||||||
<div class="intro-y datatable-wrapper box p-5 mt-5">
|
|
||||||
<table class="table table-report table-report--bordered display datatable w-full">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="border-b-2 whitespace-no-wrap">TITLE ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">COMPANY ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">CLIENT ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Requested on ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Posted on ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">STATUS ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Assignee ⨈</th>
|
|
||||||
|
|
||||||
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Applied on</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for job in jobs %}
|
|
||||||
<tr>
|
|
||||||
<td class="border-b">
|
|
||||||
<a href="{{ job.job.link }}" target="_blank" class="text-blue-500">{{ job.job.job_title [:50] }}</a>
|
|
||||||
<div class="text-gray-600 text-xs whitespace-no-wrap">{{ job.job.job_id }}</div>
|
|
||||||
</td>
|
|
||||||
<td class="text-center border-b">{{job.job.job_company}}</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
|
|
||||||
<a href="#" class="client-link text-blue-500" data-client-id="{{ job.client.id }}">{{ job.client.user_nicename }}</a>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td class="text-center border-b">{{job.job.data_requested.strftime('%Y-%m-%d')}}</td>
|
|
||||||
<td class="text-center border-b">{{job.job.date_posted.strftime('%Y-%m-%d')}}</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
<select class="select2" onchange="updateStatus(this, '{{ job.id }}')">
|
|
||||||
<option value="" {% if job.status is none %}selected{% endif %}>— Not selected —</option>
|
|
||||||
<option value="Scheduled" {% if job.status == "Scheduled" %}selected{% endif %}>Scheduled</option>
|
|
||||||
<option value="Requested" {% if job.status == "Requested" %}selected{% endif %}>Requested</option>
|
|
||||||
<option value="In-Progress" {% if job.status == "In-Progress" %}selected{% endif %}>In-Progress</option>
|
|
||||||
<option value="Applied" {% if job.status == "Applied" %}selected{% endif %}>Applied</option>
|
|
||||||
<option value="Issues Applying" {% if job.status == "Issues Applying" %}selected{% endif %}>Issues Applying</option>
|
|
||||||
<option value="Cancelled" {% if job.status == "Cancelled" %}selected{% endif %}>Cancelled</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
<select class="select2" onchange="updateAssignee(this, '{{ job.id }}')">
|
|
||||||
<option value="" {% if job.assignee is none %}selected{% endif %}>— Not selected —</option>
|
|
||||||
{% for user in users %}
|
|
||||||
<option value="{{ user.id }}" {% if job.assignee == user.id %}selected{% endif %}>
|
|
||||||
{{ user.username }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
{% if job.applied_on %}
|
|
||||||
{{ job.applied_on.strftime('%Y-%m-%d') }}
|
|
||||||
{% else %}
|
|
||||||
-
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<!-- END: Datatable -->
|
|
||||||
<!-- Включаем модалку -->
|
<!-- Включаем модалку -->
|
||||||
<!-- Модальное окно -->
|
<!-- Модальное окно -->
|
||||||
{% include "modal.html" %}
|
{% include "modal.html" %}
|
||||||
|
|
|
@ -31,74 +31,7 @@
|
||||||
|
|
||||||
{% include "filtr.html" %}
|
{% include "filtr.html" %}
|
||||||
|
|
||||||
<!-- BEGIN: Datatable -->
|
{% include "tabl.html" %}
|
||||||
<div class="intro-y datatable-wrapper box p-5 mt-5">
|
|
||||||
<table class="table table-report table-report--bordered display datatable w-full">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="border-b-2 whitespace-no-wrap">TITLE ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">COMPANY ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">CLIENT ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Requested on ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Posted on ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">STATUS ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Assignee ⨈</th>
|
|
||||||
|
|
||||||
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Applied on</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for job in jobs %}
|
|
||||||
<tr>
|
|
||||||
<td class="border-b">
|
|
||||||
<a href="{{ job.job.link }}" target="_blank" class="text-blue-500">{{ job.job.job_title [:50] }}</a>
|
|
||||||
<div class="text-gray-600 text-xs whitespace-no-wrap">{{ job.job.job_id }}</div>
|
|
||||||
</td>
|
|
||||||
<td class="text-center border-b">{{job.job.job_company}}</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
|
|
||||||
<a href="#" class="client-link text-blue-500" data-client-id="{{ job.client.id }}">{{ job.client.user_nicename }}</a>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td class="text-center border-b">{{job.job.data_requested.strftime('%Y-%m-%d')}}</td>
|
|
||||||
<td class="text-center border-b">{{job.job.date_posted.strftime('%Y-%m-%d')}}</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
<select class="select2" onchange="updateStatus(this, '{{ job.id }}')">
|
|
||||||
<option value="" {% if job.status is none %}selected{% endif %}>— Not selected —</option>
|
|
||||||
<option value="Scheduled" {% if job.status == "Scheduled" %}selected{% endif %}>Scheduled</option>
|
|
||||||
<option value="Requested" {% if job.status == "Requested" %}selected{% endif %}>Requested</option>
|
|
||||||
<option value="In-Progress" {% if job.status == "In-Progress" %}selected{% endif %}>In-Progress</option>
|
|
||||||
<option value="Applied" {% if job.status == "Applied" %}selected{% endif %}>Applied</option>
|
|
||||||
<option value="Issues Applying" {% if job.status == "Issues Applying" %}selected{% endif %}>Issues Applying</option>
|
|
||||||
<option value="Cancelled" {% if job.status == "Cancelled" %}selected{% endif %}>Cancelled</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
<select class="select2" onchange="updateAssignee(this, '{{ job.id }}')">
|
|
||||||
<option value="" {% if job.assignee is none %}selected{% endif %}>— Not selected —</option>
|
|
||||||
{% for user in users %}
|
|
||||||
<option value="{{ user.id }}" {% if job.assignee == user.id %}selected{% endif %}>
|
|
||||||
{{ user.username }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
{% if job.applied_on %}
|
|
||||||
{{ job.applied_on.strftime('%Y-%m-%d') }}
|
|
||||||
{% else %}
|
|
||||||
-
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<!-- END: Datatable -->
|
|
||||||
<!-- Включаем модалку -->
|
<!-- Включаем модалку -->
|
||||||
<!-- Модальное окно -->
|
<!-- Модальное окно -->
|
||||||
{% include "modal.html" %}
|
{% include "modal.html" %}
|
||||||
|
|
|
@ -30,74 +30,7 @@
|
||||||
|
|
||||||
|
|
||||||
{% include "filtr.html" %}
|
{% include "filtr.html" %}
|
||||||
<!-- BEGIN: Datatable -->
|
{% include "tabl.html" %}
|
||||||
<div class="intro-y datatable-wrapper box p-5 mt-5">
|
|
||||||
<table class="table table-report table-report--bordered display datatable w-full">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="border-b-2 whitespace-no-wrap">TITLE ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">COMPANY ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">CLIENT ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Requested on ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Posted on ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">STATUS ⨈</th>
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Assignee ⨈</th>
|
|
||||||
|
|
||||||
|
|
||||||
<th class="border-b-2 text-center whitespace-no-wrap">Applied on</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for job in jobs %}
|
|
||||||
<tr>
|
|
||||||
<td class="border-b">
|
|
||||||
<a href="{{ job.job.link }}" target="_blank" class="text-blue-500">{{ job.job.job_title [:50] }}</a>
|
|
||||||
<div class="text-gray-600 text-xs whitespace-no-wrap">{{ job.job.job_id }}</div>
|
|
||||||
</td>
|
|
||||||
<td class="text-center border-b">{{job.job.job_company}}</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
|
|
||||||
<a href="#" class="client-link text-blue-500" data-client-id="{{ job.client.id }}">{{ job.client.user_nicename }}</a>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td class="text-center border-b">{{job.job.data_requested.strftime('%Y-%m-%d')}}</td>
|
|
||||||
<td class="text-center border-b">{{job.job.date_posted.strftime('%Y-%m-%d')}}</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
<select class="select2" onchange="updateStatus(this, '{{ job.id }}')">
|
|
||||||
<option value="" {% if job.status is none %}selected{% endif %}>— Not selected —</option>
|
|
||||||
<option value="Scheduled" {% if job.status == "Scheduled" %}selected{% endif %}>Scheduled</option>
|
|
||||||
<option value="Requested" {% if job.status == "Requested" %}selected{% endif %}>Requested</option>
|
|
||||||
<option value="In-Progress" {% if job.status == "In-Progress" %}selected{% endif %}>In-Progress</option>
|
|
||||||
<option value="Applied" {% if job.status == "Applied" %}selected{% endif %}>Applied</option>
|
|
||||||
<option value="Issues Applying" {% if job.status == "Issues Applying" %}selected{% endif %}>Issues Applying</option>
|
|
||||||
<option value="Cancelled" {% if job.status == "Cancelled" %}selected{% endif %}>Cancelled</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
<select class="select2" onchange="updateAssignee(this, '{{ job.id }}')">
|
|
||||||
<option value="" {% if job.assignee is none %}selected{% endif %}>— Not selected —</option>
|
|
||||||
{% for user in users %}
|
|
||||||
<option value="{{ user.id }}" {% if job.assignee == user.id %}selected{% endif %}>
|
|
||||||
{{ user.username }}
|
|
||||||
</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
<td class="text-center border-b">
|
|
||||||
{% if job.applied_on %}
|
|
||||||
{{ job.applied_on.strftime('%Y-%m-%d') }}
|
|
||||||
{% else %}
|
|
||||||
-
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<!-- END: Datatable -->
|
|
||||||
<!-- Включаем модалку -->
|
<!-- Включаем модалку -->
|
||||||
<!-- Модальное окно -->
|
<!-- Модальное окно -->
|
||||||
{% include "modal.html" %}
|
{% include "modal.html" %}
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
<!-- BEGIN: Datatable -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="intro-y datatable-wrapper box p-5 mt-5">
|
||||||
|
<table class="table table-report table-report--bordered display datatable w-full">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="border-b-2 whitespace-no-wrap">TITLE ⨈</th>
|
||||||
|
<th class="border-b-2 text-center whitespace-no-wrap">COMPANY ⨈</th>
|
||||||
|
<th class="border-b-2 text-center whitespace-no-wrap">CLIENT ⨈</th>
|
||||||
|
<th class="border-b-2 text-center whitespace-no-wrap">Requested on ⨈</th>
|
||||||
|
<th class="border-b-2 text-center whitespace-no-wrap">Posted on ⨈</th>
|
||||||
|
<th class="border-b-2 text-center whitespace-no-wrap">STATUS</th>
|
||||||
|
<th class="border-b-2 text-center whitespace-no-wrap">Assignee</th>
|
||||||
|
|
||||||
|
|
||||||
|
<th class="border-b-2 text-center whitespace-no-wrap">Applied on</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for job in jobs %}
|
||||||
|
<tr>
|
||||||
|
<td class="border-b">
|
||||||
|
<a href="{{ job.job.link }}" target="_blank" class="text-blue-500">{{ job.job.job_title [:50] }}</a>
|
||||||
|
<div class="text-gray-600 text-xs whitespace-no-wrap">{{ job.job.job_id }}</div>
|
||||||
|
</td>
|
||||||
|
<td class="text-center border-b">{{job.job.job_company}}</td>
|
||||||
|
<td class="text-center border-b">
|
||||||
|
|
||||||
|
<a href="#" class="client-link text-blue-500" data-client-id="{{ job.client.id }}">{{ job.client.user_nicename }}</a>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="text-center border-b">{{job.job.data_requested.strftime('%Y-%m-%d')}}</td>
|
||||||
|
<td class="text-center border-b">{{job.job.date_posted.strftime('%Y-%m-%d')}}</td>
|
||||||
|
<td class="text-center border-b">
|
||||||
|
<select class="select2" onchange="updateStatus(this, '{{ job.id }}')">
|
||||||
|
<option value="" {% if job.status is none %}selected{% endif %} disabled>—</option>
|
||||||
|
<option value="Paused" {% if job.status == "Backlogged" %}selected{% endif %} disabled>Backlogged</option>
|
||||||
|
|
||||||
|
<option value="Scheduled" {% if job.status == "Scheduled" %}selected{% endif %} disabled>Scheduled</option>
|
||||||
|
<option value="Requested" {% if job.status == "Requested" %}selected{% endif %}>Requested</option>
|
||||||
|
<option value="In-Progress" {% if job.status == "In-Progress" %}selected{% endif %}>In-Progress</option>
|
||||||
|
|
||||||
|
<option value="Paused" {% if job.status == "Paused" %}selected{% endif %} disabled>Paused</option>
|
||||||
|
|
||||||
|
<option value="Applied" {% if job.status == "Applied" %}selected{% endif %}>Applied</option>
|
||||||
|
<option value="Issues Applying" {% if job.status == "Issues Applying" %}selected{% endif %}>Issues Applying</option>
|
||||||
|
<option value="Cancelled" {% if job.status == "Cancelled" %}selected{% endif %}>Cancelled</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td class="text-center border-b">
|
||||||
|
<select class="select2" onchange="updateAssignee(this, '{{ job.id }}')">
|
||||||
|
<option value="" {% if job.assignee is none %}selected{% endif %}>— Not selected —</option>
|
||||||
|
{% for user in users %}
|
||||||
|
<option value="{{ user.id }}" {% if job.assignee == user.id %}selected{% endif %}>
|
||||||
|
{{ user.username }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td class="text-center border-b">
|
||||||
|
{% if job.applied_on %}
|
||||||
|
{{ job.applied_on.strftime('%Y-%m-%d') }}
|
||||||
|
{% else %}
|
||||||
|
-
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- END: Datatable -->
|
12
utils/app.py
12
utils/app.py
|
@ -18,6 +18,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy.future import select
|
from sqlalchemy.future import select
|
||||||
# from model.database import create_async_engine, Job
|
# from model.database import create_async_engine, Job
|
||||||
from model.database import get_async_session, Job
|
from model.database import get_async_session, Job
|
||||||
|
from utils.telega import erro
|
||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
import os
|
import os
|
||||||
|
@ -39,9 +40,11 @@ password = os.getenv('PASSWD')
|
||||||
|
|
||||||
|
|
||||||
# Authenticate using any Linkedin user account credentials
|
# Authenticate using any Linkedin user account credentials
|
||||||
api = Linkedin(username, password)
|
|
||||||
|
|
||||||
|
api = Linkedin(username, password)
|
||||||
|
print(api)
|
||||||
async def pars_jobs(geo):
|
async def pars_jobs(geo):
|
||||||
|
try:
|
||||||
search_jobs = api.search_jobs(location_geo_id = geo)
|
search_jobs = api.search_jobs(location_geo_id = geo)
|
||||||
search_jobes = [
|
search_jobes = [
|
||||||
{
|
{
|
||||||
|
@ -63,6 +66,9 @@ async def pars_jobs(geo):
|
||||||
# json.dump(search_jobs, 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}")
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def add_to_bd():
|
def add_to_bd():
|
||||||
|
@ -250,13 +256,15 @@ async def process_jobs():
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
|
try:
|
||||||
geo_list = ['100025096', '101174742', '103644278'] # ON, Canada, USA
|
geo_list = ['100025096', '101174742', '103644278'] # ON, Canada, USA
|
||||||
for geo in geo_list:
|
for geo in geo_list:
|
||||||
await pars_jobs(geo)
|
await pars_jobs(geo)
|
||||||
await get_vakansi()
|
await get_vakansi()
|
||||||
|
|
||||||
await process_jobs() # Вызываем обработку вакансий
|
await process_jobs() # Вызываем обработку вакансий
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|
||||||
|
|
|
@ -116,7 +116,10 @@ async def del_jobs(db: AsyncSession, user_id: str):
|
||||||
jobs = await db.execute(
|
jobs = await db.execute(
|
||||||
select(AppliedJob).where(
|
select(AppliedJob).where(
|
||||||
AppliedJob.client_id == user_id,
|
AppliedJob.client_id == user_id,
|
||||||
or_(AppliedJob.status == "Scheduled", AppliedJob.status.is_(None))
|
or_(
|
||||||
|
AppliedJob.status.in_(["Scheduled", "Backlogged"]),
|
||||||
|
AppliedJob.status.is_(None)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
jobs = jobs.scalars().all()
|
jobs = jobs.scalars().all()
|
||||||
|
@ -152,7 +155,7 @@ async def del_jobs(db: AsyncSession, user_id: str):
|
||||||
|
|
||||||
async def add_jobs(db: AsyncSession, user_id: str):
|
async def add_jobs(db: AsyncSession, user_id: str):
|
||||||
# Получаем все активные вакансии (предполагаю, что ты хотел использовать True, а не 3)
|
# Получаем все активные вакансии (предполагаю, что ты хотел использовать True, а не 3)
|
||||||
query = select(Job).filter(Job.active == 3)
|
query = select(Job).where(Job.active == 3)
|
||||||
result = await db.execute(query)
|
result = await db.execute(query)
|
||||||
jobs = result.scalars().all()
|
jobs = result.scalars().all()
|
||||||
|
|
||||||
|
@ -160,9 +163,10 @@ async def add_jobs(db: AsyncSession, user_id: str):
|
||||||
return {"message": "Нет вакансий по данному фильтру"}
|
return {"message": "Нет вакансий по данному фильтру"}
|
||||||
|
|
||||||
applied_jobs = []
|
applied_jobs = []
|
||||||
|
scheduled_count = 0
|
||||||
|
|
||||||
for job in jobs:
|
for job in jobs:
|
||||||
# Проверка, существует ли уже AppliedJob с таким client_id и job_id
|
# Проверяем, существует ли уже такая пара client_id + job_id
|
||||||
check_query = select(AppliedJob).where(
|
check_query = select(AppliedJob).where(
|
||||||
and_(
|
and_(
|
||||||
AppliedJob.client_id == user_id,
|
AppliedJob.client_id == user_id,
|
||||||
|
@ -173,16 +177,20 @@ async def add_jobs(db: AsyncSession, user_id: str):
|
||||||
existing = check_result.scalar_one_or_none()
|
existing = check_result.scalar_one_or_none()
|
||||||
|
|
||||||
if not existing:
|
if not existing:
|
||||||
|
# Первые 5 добавим со статусом Scheduled, остальные Backlogged
|
||||||
|
status = "Scheduled" if scheduled_count < 5 else "Backlogged"
|
||||||
applied_jobs.append(
|
applied_jobs.append(
|
||||||
AppliedJob(client_id=user_id, job_id=job.job_id, status="Scheduled")
|
AppliedJob(client_id=user_id, job_id=job.job_id, status=status)
|
||||||
)
|
)
|
||||||
|
if status == "Scheduled":
|
||||||
|
scheduled_count += 1
|
||||||
|
|
||||||
if applied_jobs:
|
if applied_jobs:
|
||||||
db.add_all(applied_jobs)
|
db.add_all(applied_jobs)
|
||||||
await db.commit()
|
await db.commit()
|
||||||
print (f'{"message": f"{len(applied_jobs)} вакансий добавлено в AppliedJob"}')
|
return {"message": f"{len(applied_jobs)} вакансий добавлено в AppliedJob (Scheduled: {scheduled_count})"}
|
||||||
else:
|
else:
|
||||||
print (f'{"message": "Все вакансии уже были добавлены ранее"}')
|
return {"message": "Все вакансии уже были добавлены ранее"}
|
||||||
|
|
||||||
|
|
||||||
async def get_applied_jobs(db, client_id: int):
|
async def get_applied_jobs(db, client_id: int):
|
||||||
|
@ -208,6 +216,7 @@ async def get_applied_jobs(db, client_id: int):
|
||||||
"title": job.job_title,
|
"title": job.job_title,
|
||||||
"company": job.job_company,
|
"company": job.job_company,
|
||||||
"postedDate": job.date_posted,
|
"postedDate": job.date_posted,
|
||||||
|
"AppliedDate": applied.applied_on,
|
||||||
"location": job.location,
|
"location": job.location,
|
||||||
"jobType": job.job_type,
|
"jobType": job.job_type,
|
||||||
"salary": f"${job.minimum_annual_salary}K" if job.minimum_annual_salary else "Not specified",
|
"salary": f"${job.minimum_annual_salary}K" if job.minimum_annual_salary else "Not specified",
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||||
|
|
||||||
|
from model.database import AppliedJob, Job, async_session
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from sqlalchemy import select, update, func
|
||||||
|
from sqlalchemy.orm import joinedload
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
|
||||||
|
# from model.database import AppliedJob, Job
|
||||||
|
from sqlalchemy.sql import and_
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Загружаем переменные окружения
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
# Правильная строка подключения
|
||||||
|
DATABASE_URL = os.getenv('DATABASE_URL') # Должно быть "mysql+aiomysql://..." или "postgresql+asyncpg://..."
|
||||||
|
|
||||||
|
# Создаём асинхронный движок
|
||||||
|
# async_engine = create_async_engine(DATABASE_URL, echo=True)
|
||||||
|
async_engine = create_async_engine(DATABASE_URL, echo=True, pool_recycle=1800, pool_size=10, pool_pre_ping=True )
|
||||||
|
|
||||||
|
|
||||||
|
# Создаём фабрику сессий
|
||||||
|
async_session = sessionmaker(async_engine, class_=AsyncSession, expire_on_commit=False)
|
||||||
|
|
||||||
|
async def promote_backlogged_to_scheduled(db: async_session):
|
||||||
|
# Получаем всех уникальных client_id, у которых есть Backlogged
|
||||||
|
result = await db.execute(
|
||||||
|
select(AppliedJob.client_id)
|
||||||
|
.where(AppliedJob.status == "Backlogged")
|
||||||
|
.distinct()
|
||||||
|
)
|
||||||
|
client_ids = [row[0] for row in result.fetchall()]
|
||||||
|
|
||||||
|
for client_id in client_ids:
|
||||||
|
# Выбираем до 5 самых новых Backlogged вакансий для клиента
|
||||||
|
result = await db.execute(
|
||||||
|
select(AppliedJob)
|
||||||
|
.join(AppliedJob.job) # ⬅️ Явно присоединяем таблицу jobs
|
||||||
|
.options(joinedload(AppliedJob.job)) # Чтобы подгрузить Job в объект
|
||||||
|
.where(
|
||||||
|
and_(
|
||||||
|
AppliedJob.client_id == client_id,
|
||||||
|
AppliedJob.status == "Backlogged"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.order_by(Job.date_posted.desc())
|
||||||
|
.limit(5)
|
||||||
|
)
|
||||||
|
jobs_to_update = result.scalars().all()
|
||||||
|
|
||||||
|
for job in jobs_to_update:
|
||||||
|
job.status = "Scheduled" # Меняем статус
|
||||||
|
|
||||||
|
await db.commit()
|
||||||
|
return {"message": f"Обновлено клиентов: {len(client_ids)}"}
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
async with async_session() as session:
|
||||||
|
await promote_backlogged_to_scheduled(session)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
|
@ -0,0 +1,26 @@
|
||||||
|
import requests
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
api_key = os.getenv('TGAPI_KEY')
|
||||||
|
chat = os.getenv('TGCHAT_ID')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# text = '=> Народ 🥺 упал обмен с контактами не забудь поднять'
|
||||||
|
text1 = ' => Упс 🆘 <ins><b>TECT </b></ins> ! 🥺"'
|
||||||
|
# erro(text1=' => Упс 🆘 <ins><b>TECT </b></ins> ! 🥺')
|
||||||
|
def erro(text1):
|
||||||
|
a = requests.get('https://api.telegram.org/bot{}/sendMessage'.format(api_key), params=dict(
|
||||||
|
chat_id=chat,
|
||||||
|
text= str(text1),
|
||||||
|
parse_mode='HTML'))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# if __name__ == '__main__':
|
||||||
|
# erro(text1)
|
Loading…
Reference in New Issue