7 Commits
11 changed files with 95 additions and 53 deletions
+1
View File
@@ -1 +1,2 @@
__pycache__ __pycache__
venv/
+19 -3
View File
@@ -1,7 +1,7 @@
<br> <br>
<p align="center"> <p align="center">
<img align="center" src="media/cover.png"> <img align="center" src="media/cover.png">
<h3 align="center">VATSIM Fox</h3> <h3 align="center">VATSIM Fox — v1.2</h3>
<p align="center">Website to find out your detailed VATSIM stastic</p> <p align="center">Website to find out your detailed VATSIM stastic</p>
</p> </p>
<br> <br>
@@ -11,10 +11,10 @@
Project based on [Virtual Air Traffic Simulation Network (VATSIM) API](https://vatsim.dev/). Project based on [Virtual Air Traffic Simulation Network (VATSIM) API](https://vatsim.dev/).
Written in Python 3.7.10 + JS (JQuery framework) + Boostrap Written in Python 3.8.18 + JS (JQuery framework) + Boostrap
## Startup ## Startup
For run application on your own machine, you must install docker. Then just one command in the "src" folder with the project source code: To run application on your own machine, you must install docker. Then just one command in the "src" folder with the project source code:
```bash ```bash
docker compose up docker compose up
@@ -25,6 +25,22 @@ Any help with this project would be greatly appreciated. Fixes or some features
VATSIM Fox is free software, open to suggestions and contributions. All the files are under the MIT License. VATSIM Fox is free software, open to suggestions and contributions. All the files are under the MIT License.
### How to set virtual environment
```bash
# you must be in "src" folder before init
cd src
python -m venv venv
.\venv\Scripts\activate
# downloading requirements && upgrading pip
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
# starting application
python -m flask run --host 0.0.0.0 --debug
```
## Feature ## Feature
There is small TODO list I would like to make in this project: There is small TODO list I would like to make in this project:
-1
View File
@@ -1,3 +1,2 @@
[flake8] [flake8]
exclude = .git, pycache, .env, .venv exclude = .git, pycache, .env, .venv
max-line-length = 120
+1 -1
View File
@@ -1,4 +1,4 @@
FROM python:3.7.10 FROM python:3.8.18
WORKDIR /build/ WORKDIR /build/
+9 -8
View File
@@ -1,5 +1,5 @@
from flask import Flask, render_template, abort, request, jsonify
import requests import requests
from flask import Flask, render_template, abort, request, jsonify
app = Flask(__name__) app = Flask(__name__)
@@ -15,15 +15,16 @@ def index_page():
def errorhandler_page(_): def errorhandler_page(_):
return render_template("4O4.html"), 404 return render_template("4O4.html"), 404
# Custom wrapper to fetch VATSIM data
@app.route("/api/request") @app.route("/api/request")
def request_handler(): def request_handler():
request_string = request.args.get("request_string") if request_string := request.args.get("request_string"):
try:
return requests.get(f"{VATSIM_API_URL}{request_string}").json()
except requests.exceptions.JSONDecodeError:
return jsonify({"result": "error", "message": "Something went wrong. Report that error please"})
if request_string: return jsonify({"result": "not found", "message": "Not found"})
return requests.get(f"{VATSIM_API_URL}{request_string}").json()
return jsonify({"result": "not found"})
@app.route("/<int:cid>/") @app.route("/<int:cid>/")
@@ -33,7 +34,7 @@ def viewer_page(cid):
if "detail" in user and user["detail"] == "Not Found": if "detail" in user and user["detail"] == "Not Found":
return abort(404) return abort(404)
return render_template("viewer.html", data=(user, )) return render_template("viewer.html", user=user)
if __name__ == "__main__": if __name__ == "__main__":
+4
View File
@@ -6,3 +6,7 @@
.fox_active, .fox_atc_active { .fox_active, .fox_atc_active {
background-color: #dee2e6 !important; background-color: #dee2e6 !important;
} }
#notification_wrapper {
display: none;
}
+13 -5
View File
@@ -2,8 +2,10 @@
let userSession = {}; let userSession = {};
fetch(ATC_API_URL, {}).then(response => response.json()).then(result => { fetch(ATC_API_URL, {}).then(response => response.json()).then(result => {
if (result && result["result"] == "not found") if (result && (result["result"] == "not found" || result["result"] == "error")) {
return showAlert("Error while getting ATC information", result["message"]);
return;
}
for (let session of result["items"]) { for (let session of result["items"]) {
// Separate each session by it's connection ID // Separate each session by it's connection ID
@@ -72,13 +74,13 @@ $.getJSON("/static/data/airports.json", function(json) {
// Map creation // Map creation
const MAP_ELEMENT = L.map("fox_map"); const MAP_ELEMENT = L.map("fox_map");
const MAP_DEFAULT_TARGET = L.latLng("47.50737", "19.04611"); const MAP_DEFAULT_ZOOM_VIEW = 2;
const MAP_DEFAULT_ZOOM_VIEW = 14; const MAP_DEFAULT_TARGET = L.latLng("0", "0");
let line = null; let line = null;
MAP_ELEMENT.setView(MAP_DEFAULT_TARGET, MAP_DEFAULT_ZOOM_VIEW); MAP_ELEMENT.setView(MAP_DEFAULT_TARGET, MAP_DEFAULT_ZOOM_VIEW);
L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png", { L.tileLayer("https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png", {
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors', attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
subdomains: "abcd", subdomains: "abcd",
maxZoom: 20, maxZoom: 20,
@@ -153,3 +155,9 @@ function showSession(session) {
$(".atc_transfers").text(session["handoffsinitiated"]); $(".atc_transfers").text(session["handoffsinitiated"]);
$(".atc_received").text(session["handoffsreceived"]); $(".atc_received").text(session["handoffsreceived"]);
} }
function showAlert(title, message) {
$("#notification_wrapper").css("display", "block");
$("#notification_title").text(title);
$("#notification_message").text(message);
}
+6 -6
View File
@@ -2,11 +2,11 @@
{% block title %}404 - Not Found / VATSIM Fox{% endblock %} {% block title %}404 - Not Found / VATSIM Fox{% endblock %}
{% block content %} {% block content %}
<div class="p-5 mb-4 bg-light rounded-3"> <div class="p-5 mb-4 bg-light rounded-3">
<div class="container-fluid py-5"> <div class="container-fluid py-5">
<h1 class="mb-4 display-5 fw-bold">4O4 - Page Not Found</h1> <h1 class="mb-4 display-5 fw-bold">4O4 - Page Not Found</h1>
<a href="/"> <a href="/">
<button class="btn btn-outline-primary" type="submit">Back to index page</button> <button class="btn btn-outline-primary" type="submit">Back to index page</button>
</a> </a>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
+23 -17
View File
@@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="description" content="VATSIM Fox — see your detailed profile statistic"> <meta name="description" content="VATSIM Fox — check your detailed profile statistic">
<meta name="keywords" content="VATSIM FOX, fox, vatsim, vatsim statistic, statistic, vatsim fox, flight simulator"> <meta name="keywords" content="VATSIM FOX, fox, vatsim, vatsim statistic, statistic, vatsim fox, flight simulator">
<meta name="author" content="uw935"> <meta name="author" content="uw935">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -18,34 +18,40 @@
{% block header_css %}{% endblock %} {% block header_css %}{% endblock %}
</head> </head>
<style> <style>
:root { .btn-outline-primary:hover, .btn-outline-primary:active {
--bs-primary-rgb: 250, 130, 50 color: white !important;
} background-color: var(--main-rgb) !important;
border-color: var(--main-rgb) !important;
}
.btn-outline-primary {
border-color: var(--main-rgb);
color: var(--main-rgb);
}
:root {
--main-rgb: #FA8232;
--bs-primary-rgb: 250, 130, 50
}
</style> </style>
<body> <body>
<main> <main>
<div class="container py-4"> <div class="container py-4">
<header class="pb-3 mb-4 border-bottom"> <header class="pb-3 mb-4 border-bottom">
<a href="/" class="d-flex align-items-center text-dark text-decoration-none"> <a href="/" class="d-flex align-items-center text-dark text-decoration-none">
<!-- <svg xmlns="http://www.w3.org/2000/svg" width="40" height="32" class="me-2" viewBox="0 0 118 94" role="img"><title>Bootstrap</title><path fill-rule="evenodd" clip-rule="evenodd" d="M24.509 0c-6.733 0-11.715 5.893-11.492 12.284.214 6.14-.064 14.092-2.066 20.577C8.943 39.365 5.547 43.485 0 44.014v5.972c5.547.529 8.943 4.649 10.951 11.153 2.002 6.485 2.28 14.437 2.066 20.577C12.794 88.106 17.776 94 24.51 94H93.5c6.733 0 11.714-5.893 11.491-12.284-.214-6.14.064-14.092 2.066-20.577 2.009-6.504 5.396-10.624 10.943-11.153v-5.972c-5.547-.529-8.934-4.649-10.943-11.153-2.002-6.484-2.28-14.437-2.066-20.577C105.214 5.894 100.233 0 93.5 0H24.508zM80 57.863C80 66.663 73.436 72 62.543 72H44a2 2 0 01-2-2V24a2 2 0 012-2h18.437c9.083 0 15.044 4.92 15.044 12.474 0 5.302-4.01 10.049-9.119 10.88v.277C75.317 46.394 80 51.21 80 57.863zM60.521 28.34H49.948v14.934h8.905c6.884 0 10.68-2.772 10.68-7.727 0-4.643-3.264-7.207-9.012-7.207zM49.948 49.2v16.458H60.91c7.167 0 10.964-2.876 10.964-8.281 0-5.406-3.903-8.178-11.425-8.178H49.948z" fill="currentColor"></path></svg> -->
<span class="fs-4">🦊 VATSIM <span class="text-primary">Fox</span></span> <span class="fs-4">🦊 VATSIM <span class="text-primary">Fox</span></span>
</a> </a>
</header> </header>
<!-- <div class="mb-4 alert alert-primary" role="alert">
Notification
</div>
-->
{% block content %}{% endblock %} {% block content %}{% endblock %}
<footer class="pt-3 mt-4 text-muted border-top"> <footer class="pt-3 mt-4 text-muted border-top">
created by <a href="https://uw935.t.me/" style="text-decoration: underline; cursor: pointer;">uw935</a> (1606255) created by <a href="https://uw935.t.me/" style="text-decoration: underline; cursor: pointer;">uw935</a> (1606255)
</footer> </footer>
</div> </div>
</main> </main>
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script> <script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
{% block javascript_code %}{% endblock %} {% block javascript_code %}{% endblock %}
</body> </body>
</html> </html>
+5 -5
View File
@@ -7,7 +7,7 @@
<p class="col-md-8 fs-4">See your detailed profile statistic</p> <p class="col-md-8 fs-4">See your detailed profile statistic</p>
<div class="d-flex" role="search"> <div class="d-flex" role="search">
<input class="fox_input form-control me-2" type="number" placeholder="100000" aria-label="CID"> <input class="fox_input form-control me-2" type="number" placeholder="100000" aria-label="CID">
<button class="fox_submit btn btn-outline-success" type="submit">Search</button> <button class="fox_submit btn btn-outline-primary" type="submit">Search</button>
</div> </div>
<p class="fox_message text-primary col-md-8 fs-4"></p> <p class="fox_message text-primary col-md-8 fs-4"></p>
</div> </div>
@@ -34,11 +34,11 @@
{% block javascript_code %} {% block javascript_code %}
<script type="text/javascript"> <script type="text/javascript">
$(".fox_submit").click(function() { $(".fox_submit").click(function() {
const INPUT_CID = 0 + $(".fox_input").val(); const INPUT_CID = $(".fox_input").val();
if ((0 + INPUT_CID) < 100000) { if (INPUT_CID < 100000) {
$(".fox_message").text("CID should be equal or more than 100000"); $(".fox_message").text("CID should be equal or more than 100000");
return; return;
} }
fetch("/api/request?request_string=members/" + INPUT_CID, {}) fetch("/api/request?request_string=members/" + INPUT_CID, {})
+12 -5
View File
@@ -1,12 +1,19 @@
{% extends "/include/template.html" %} {% extends "/include/template.html" %}
{% block title %}User {{user_cid}} statistic — VATSIM Fox{% endblock %} {% block title %}User {{user["id"]}} statistic — VATSIM Fox{% endblock %}
{% block header_css %} {% block header_css %}
<link rel="stylesheet" href="{{ url_for('static', filename='viewer.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='viewer.css') }}">
<link href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" rel="stylesheet"/> <link href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" rel="stylesheet"/>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="mb-5"> <div class="mb-5">
<h2 class="text-center text-primary mb-4">{{data[0]["id"]}}</h2> <div id="notification_wrapper">
<div class="mb-4 alert alert-warning" role="alert">
<h4 id="notification_title"></h4>
<span id="notification_message"></span>
</div>
</div>
<h2 class="text-center text-primary mb-4">{{user["id"]}}</h2>
<p class="fs-4 text-secondary">Date of registration: <span class="date_registration"></span></p> <p class="fs-4 text-secondary">Date of registration: <span class="date_registration"></span></p>
</div> </div>
@@ -91,7 +98,7 @@
<div class="col-md" style="height: 321px;"> <div class="col-md" style="height: 321px;">
<div class="h-auto p-5 bg-light border rounded-3"> <div class="h-auto p-5 bg-light border rounded-3">
<h2 class="atc_callsign">UUEE_DEL</h2> <h2 class="atc_callsign">Callsign</h2>
<span> <span>
<b>Start time:</b> <b>Start time:</b>
<span class="atc_start_time"></span> <span class="atc_start_time"></span>
@@ -133,11 +140,11 @@
{% block javascript_code %} {% block javascript_code %}
<script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js"></script> <script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js"></script>
<script type="text/javascript"> <script type="text/javascript">
const USER_CID = '{{data[0]["id"]}}'; const USER_CID = '{{user["id"]}}';
const FLIGHTPLANS_API_URL = "/api/request?request_string=members/" + USER_CID + "/flightplans"; const FLIGHTPLANS_API_URL = "/api/request?request_string=members/" + USER_CID + "/flightplans";
const ATC_API_URL = "/api/request?request_string=members/" + USER_CID + "/atc"; const ATC_API_URL = "/api/request?request_string=members/" + USER_CID + "/atc";
$(".date_registration").text("{{data[0]['reg_date']}}".replace("T", " ")); $(".date_registration").text("{{user['reg_date']}}".replace("T", " "));
</script> </script>
<script type="text/javascript" src="{{ url_for('static', filename='viewer.js') }}"></script> <script type="text/javascript" src="{{ url_for('static', filename='viewer.js') }}"></script>
{% endblock %} {% endblock %}