Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import io
from flask import Flask
from flask import render_template
from flask import Response
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from services import get_cv, get_count_by_year, create_random_figure, get_top_edu_by_job

app = Flask(__name__)


@app.route("/")
def cv_index():
cvs = get_cv()
res = ("\n".join((f"<h1>{i + 1})</h1>",
f"<p>Желаемая зарплата: {cv['salary']}.</p>",
f"<p>Образование: {cv['educationType']}.</p>")) for i, cv in enumerate(cvs))
return "".join(res)


@app.route("/dashboard")
def dashboard():
return render_template('d3.html', cvs=get_cv())


@app.route("/years")
def years():
cvs = get_count_by_year()
data = [cv["count"] for cv in cvs]
labels = [cv["year"] for cv in cvs]
return render_template('d4.html', cvs=cvs, data=data, labels=labels)


@app.route("/edu/managers")
def edu_managers():
cvs = get_top_edu_by_job(("manager", "менедж"))
data = [cv["count"] for cv in cvs]
labels = [cv["qualification"] for cv in cvs]
return render_template('d5.html', cvs=cvs, data=data, labels=labels, ql="менеджеров")


@app.route("/edu/engineer")
def edu_engineers():
cvs = get_top_edu_by_job(("engineer", "инжене"))
data = [cv["count"] for cv in cvs]
labels = [cv["qualification"] for cv in cvs]
return render_template('d5.html', cvs=cvs, data=data, labels=labels, ql="инженеров")


@app.route('/plot.png')
def plot_png():
output = io.BytesIO()
FigureCanvas(create_random_figure()).print_png(output)
return Response(output.getvalue(), mimetype='image/png')


def main() -> None:
app.run()


if __name__ == "__main__":
main()
60 changes: 60 additions & 0 deletions services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import sqlite3
import random
from matplotlib.figure import Figure

DB_NAME = 'works.sqlite'


def _ignore_case_collation(value1_, value2_):
value1_, value2_ = str(value1_), str(value2_)
if value1_.lower() == value2_.lower():
return 0
elif value1_.lower() < value2_.lower():
return -1
else:
return 1


def get_connection():
con = sqlite3.connect(DB_NAME)
con.row_factory = lambda cursor, row: {col[0]: row[idx] for idx, col in enumerate(cursor.description)}
# Изначально sqlite работает только с ASCII символами.
con.create_collation("BINARY", _ignore_case_collation)
con.create_collation("NOCASE", _ignore_case_collation)
con.create_function("LOWER", 1, lambda value_: str(value_).lower())
con.create_function("UPPER", 1, lambda value_: str(value_).upper())

return con


def get_cv():
query = "SELECT * FROM works LIMIT 20"
with get_connection() as con:
return list(con.execute(query))


def get_count_by_year():
query = ("SELECT COUNT(*) as 'count', strftime('%Y', dateModify) as 'year' "
"FROM works WHERE year IS NOT NULL GROUP BY year")
with get_connection() as con:
return list(con.execute(query))


def get_top_edu_by_job(job_titles: tuple[str, str], count: int = 15):
query = (f"SELECT LOWER(qualification) as 'qualification', count(*) as 'count' FROM works "
f"WHERE qualification IS NOT NULL AND "
f"(LOWER(jobTitle) like '%{job_titles[0]}%' OR LOWER(jobTitle) like '%{job_titles[1]}%')"
f"GROUP BY LOWER(qualification) "
f"ORDER BY count DESC LIMIT {count}")
with get_connection() as con:
return list(con.execute(query))


def create_random_figure():
fig = Figure()
axis = fig.add_subplot(1, 1, 1)
xs = range(100)
ys = [random.randint(1, 50) for _ in xs]
axis.plot(xs, ys)

return fig
226 changes: 226 additions & 0 deletions templates/d4.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
<meta name="generator" content="Hugo 0.88.1">
<title>Dashboard Template · Bootstrap v5.1</title>

<link rel="canonical" href="https://getbootstrap.com/docs/5.1/examples/dashboard/">



<!-- Bootstrap core CSS -->
<link href="https://getbootstrap.com/docs/5.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">

<!-- Favicons -->
<link rel="apple-touch-icon" href="https://getbootstrap.com/docs/5.1/assets/img/favicons/apple-touch-icon.png" sizes="180x180">
<link rel="icon" href="https://getbootstrap.com/docs/5.1/assets/img/favicons/favicon-32x32.png" sizes="32x32" type="image/png">
<link rel="icon" href="https://getbootstrap.com/docs/5.1/assets/img/favicons/favicon-16x16.png" sizes="16x16" type="image/png">
<link rel="manifest" href="https://getbootstrap.com/docs/5.1/assets/img/favicons/manifest.json">
<link rel="mask-icon" href="https://getbootstrap.com/docs/5.1/assets/img/favicons/safari-pinned-tab.svg" color="#7952b3">
<link rel="icon" href="https://getbootstrap.com/docs/5.1/assets/img/favicons/favicon.ico">
<meta name="theme-color" content="#7952b3">


<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}

@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
</style>


<!-- Custom styles for this template -->
<link href="dashboard.css" rel="stylesheet">
</head>
<body>

<header class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-md-3 col-lg-2 me-0 px-3" href="#">База резюме</a>
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
<div class="navbar-nav">
<div class="nav-item text-nowrap">
<a class="nav-link px-3" href="#">Sign out</a>
</div>
</div>
</header>

<div class="container-fluid">
<div class="row">
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
<div class="position-sticky pt-3">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">
<span data-feather="home"></span>
Dashboard
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="file"></span>
Orders
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="shopping-cart"></span>
Products
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="users"></span>
Customers
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="bar-chart-2"></span>
Reports
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="layers"></span>
Integrations
</a>
</li>
</ul>

<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>Saved reports</span>
<a class="link-secondary" href="#" aria-label="Add a new report">
<span data-feather="plus-circle"></span>
</a>
</h6>
<ul class="nav flex-column mb-2">
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="file-text"></span>
Current month
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="file-text"></span>
Last quarter
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="file-text"></span>
Social engagement
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="file-text"></span>
Year-end sale
</a>
</li>
</ul>
</div>
</nav>

<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Dashboard</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group me-2">
<button type="button" class="btn btn-sm btn-outline-secondary">Share</button>
<button type="button" class="btn btn-sm btn-outline-secondary">Export</button>
</div>
<button type="button" class="btn btn-sm btn-outline-secondary dropdown-toggle">
<span data-feather="calendar"></span>
This week
</button>
</div>
</div>

<canvas class="my-4 w-100" id="myChart" width="900" height="380"></canvas>

<h2>Количество резюме по годам</h2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Год</th>
<th scope="col">Количество</th>
</tr>
</thead>
<tbody>
{% for cv in cvs %}
<tr>
<td>{{ loop.index }}</td>
<td>{{ cv.year }}</td>
<td>{{ cv.count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</main>
</div>
</div>


<script src="https://getbootstrap.com/docs/5.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>

<script src="https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/feather.min.js" integrity="sha384-uO3SXW5IuS1ZpFPKugNNWqTZRRglnUJK6UAZ/gxOX80nxEkN9NcGZTftn6RzhGWE" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.4/dist/Chart.min.js" integrity="sha384-zNy6FEbO50N+Cg5wap8IKA4M/ZnLJgzc6w2NqACZaK0u0FXfOWRRJOnQtpZun8ha" crossorigin="anonymous"></script>
<script>
/* globals Chart:false, feather:false */
(function () {
'use strict'

feather.replace({ 'aria-hidden': 'true' })

// Graphs
var ctx = document.getElementById('myChart')
// eslint-disable-next-line no-unused-vars
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: {{ labels|safe}},
datasets: [{
data: {{ data|safe}},
lineTension: 0,
backgroundColor: 'transparent',
borderColor: '#007bff',
borderWidth: 4,
pointBackgroundColor: '#007bff'
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: false
}
}]
},
legend: {
display: false
}
}
})
})()
</script>
</body>
</html>
Loading