Initial commit
This commit is contained in:
commit
7825abf11c
3
config.py
Normal file
3
config.py
Normal file
@ -0,0 +1,3 @@
|
||||
DEBUG = False
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
SQLALCHEMY_DATABASE_URI = 'sqlite:///../discworld.db'
|
BIN
discworld.db
Normal file
BIN
discworld.db
Normal file
Binary file not shown.
9
discworld/__init__.py
Normal file
9
discworld/__init__.py
Normal file
@ -0,0 +1,9 @@
|
||||
from flask import Flask
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_object('config')
|
||||
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
from . import views
|
95
discworld/models.py
Normal file
95
discworld/models.py
Normal file
@ -0,0 +1,95 @@
|
||||
from datetime import datetime
|
||||
|
||||
from . import (
|
||||
db,
|
||||
shop
|
||||
)
|
||||
|
||||
|
||||
class ShopProduct(db.Model):
|
||||
id = db.Column(db.Integer, primary_key = True)
|
||||
name = db.Column(db.String(100), unique=True, nullable=False)
|
||||
total_listed = db.Column(db.Integer)
|
||||
total_sold = db.Column(db.Integer)
|
||||
entries = db.relationship('ShopEntry', backref='product', lazy=True,
|
||||
order_by="desc(ShopEntry.date)")
|
||||
|
||||
def __repr__(self):
|
||||
return '<ShopProduct "{}">'.format(self.name)
|
||||
|
||||
@property
|
||||
def latest_entry(self):
|
||||
return ShopEntry.query.filter_by(
|
||||
product_id=self.id).order_by(ShopEntry.date.desc()).first()
|
||||
|
||||
@property
|
||||
def total_stocked(self):
|
||||
entries = ShopEntry.query.filter_by(product_id=self.id).order_by(
|
||||
ShopEntry.date.asc())
|
||||
|
||||
last_stock = 0
|
||||
stocked_count = 0
|
||||
for entry in entries:
|
||||
diff = last_stock - entry.stock
|
||||
if diff < 0:
|
||||
stocked_count += abs(diff)
|
||||
last_stock = entry.stock
|
||||
return stocked_count
|
||||
|
||||
@property
|
||||
def total_sold(self):
|
||||
entries = ShopEntry.query.filter_by(product_id=self.id).order_by(
|
||||
ShopEntry.date.asc())
|
||||
|
||||
last_stock = 0
|
||||
sold_count = 0
|
||||
for entry in entries:
|
||||
diff = last_stock - entry.stock
|
||||
if diff > 0:
|
||||
sold_count += diff
|
||||
last_stock = entry.stock
|
||||
return sold_count
|
||||
|
||||
@property
|
||||
def total_earned(self):
|
||||
entries = ShopEntry.query.filter_by(product_id=self.id).order_by(
|
||||
ShopEntry.date.asc())
|
||||
|
||||
last_stock = 0
|
||||
earned_count = 0.00
|
||||
for entry in entries:
|
||||
diff = last_stock - entry.stock
|
||||
if diff > 0:
|
||||
earned_count += (entry.price * diff)
|
||||
last_stock = entry.stock
|
||||
return earned_count
|
||||
|
||||
|
||||
class ShopEntry(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
stock = db.Column(db.Integer, unique=False, nullable=False)
|
||||
raw_price = db.Column(db.String(20), unique=False, nullable=False)
|
||||
raw_stock = db.Column(db.String(2), unique=False, nullable=False)
|
||||
date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
|
||||
product_id = db.Column(db.Integer, db.ForeignKey('shop_product.id'),
|
||||
nullable=False)
|
||||
|
||||
def __repr__(self):
|
||||
return '<ShopEntry "{}" {}>'.format(
|
||||
self.product.name,
|
||||
self.id
|
||||
)
|
||||
|
||||
@property
|
||||
def stock(self):
|
||||
stock_map = {
|
||||
'zero': 0, 'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5,
|
||||
'six': 6, 'seven': 7, 'eight': 8, 'nine': 9, 'ten': 10,
|
||||
'eleven': 11, 'twelve': 12, 'thirteen': 13, 'fourteen': 14
|
||||
}
|
||||
return stock_map[self.raw_stock]
|
||||
|
||||
@property
|
||||
def price(self):
|
||||
brass = shop.convert_lancre_to_brass(self.raw_price)
|
||||
return shop.convert_brass_to_am(brass)
|
97
discworld/shop.py
Normal file
97
discworld/shop.py
Normal file
@ -0,0 +1,97 @@
|
||||
import re
|
||||
|
||||
from . import (
|
||||
db,
|
||||
models
|
||||
)
|
||||
|
||||
|
||||
def convert_lancre_to_brass(raw_price):
|
||||
coins = {
|
||||
'penny': 0,
|
||||
'shilling': 0,
|
||||
'crown': 0,
|
||||
'sovereign': 0,
|
||||
'hedgehog': 0
|
||||
}
|
||||
|
||||
def noz(num):
|
||||
if num and num is not '-':
|
||||
return int(num)
|
||||
return 0
|
||||
|
||||
# Figure out which special notation we have
|
||||
if "LC" in raw_price:
|
||||
pattern = r'^LC (\d+)\|(\d+|-)\|(\d+|-)$'
|
||||
match = re.match(pattern, raw_price)
|
||||
groups = match.groups()
|
||||
|
||||
coins['penny'] = noz(groups[2])
|
||||
coins['shilling'] = noz(groups[1])
|
||||
coins['crown'] = noz(groups[0])
|
||||
elif "LSov" in raw_price:
|
||||
pattern = r'^LSov (\d+)\|(\d+|-)\|(\d+|-)\|(\d+|-)$'
|
||||
match = re.match(pattern, raw_price)
|
||||
groups = match.groups()
|
||||
|
||||
coins['penny'] = noz(groups[3])
|
||||
coins['shilling'] = noz(groups[2])
|
||||
coins['crown'] = noz(groups[1])
|
||||
coins['sovereign'] = noz(groups[0])
|
||||
elif "LH" in raw_price:
|
||||
pattern = r'^LH (\d+)\|(\d+|-)\|(\d+|-)\|(\d+|-)\|(\d+|-)$'
|
||||
match = re.match(pattern, raw_price)
|
||||
groups = match.groups()
|
||||
|
||||
coins['penny'] = noz(groups[4])
|
||||
coins['shilling'] = noz(groups[3])
|
||||
coins['crown'] = noz(groups[2])
|
||||
coins['sovereign'] = noz(groups[1])
|
||||
coins['hedgehog'] = noz(groups[0])
|
||||
|
||||
# Convert to brass
|
||||
brass_coins = (
|
||||
(coins['hedgehog'] * 248832) +
|
||||
(coins['sovereign'] * 20736) +
|
||||
(coins['crown'] * 1728) +
|
||||
(coins['shilling'] * 144) +
|
||||
(coins['penny'] * 12)
|
||||
)
|
||||
return brass_coins
|
||||
|
||||
def convert_brass_to_am(brass_price):
|
||||
# 12 brass coins to am pennies
|
||||
pennies = brass_price / 4
|
||||
return (pennies/100)
|
||||
|
||||
def parse_shop_output(data):
|
||||
pattern = r'^\s{3}?\w{2}\)\sAn*\s([\w\s-]+) for (L\w{1,3} [\d\-|]+);\s(\w+)\sleft\.$'
|
||||
matches = [m.groups() for m in re.finditer(pattern, data, re.MULTILINE)]
|
||||
|
||||
if not matches:
|
||||
return
|
||||
|
||||
# Iterate over each product line in the data
|
||||
seen_products = []
|
||||
for m in matches:
|
||||
product = models.ShopProduct.query.filter_by(name=m[0]).first()
|
||||
if not product:
|
||||
# If we didn't find a product, create it
|
||||
product = models.ShopProduct(name=m[0])
|
||||
db.session.add(product)
|
||||
# Add a ShopEntry for this row only if stock has changed
|
||||
if not product.latest_entry or product.latest_entry.raw_stock != m[2]:
|
||||
entry = models.ShopEntry(raw_price=m[1], raw_stock=m[2], product=product)
|
||||
db.session.add(entry)
|
||||
seen_products.append(product)
|
||||
|
||||
# Check all products against seen, record sellouts
|
||||
products = models.ShopProduct.query.all()
|
||||
for product in products:
|
||||
if product not in seen_products and product.latest_entry.stock != 0:
|
||||
entry = models.ShopEntry(
|
||||
raw_price=product.latest_entry.raw_price,
|
||||
raw_stock='zero', product=product
|
||||
)
|
||||
db.session.add(entry)
|
||||
db.session.commit()
|
BIN
discworld/static/favicon.ico
Normal file
BIN
discworld/static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 785 B |
76
discworld/templates/layout.html
Normal file
76
discworld/templates/layout.html
Normal file
@ -0,0 +1,76 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Ramtops Remedies and Reagents</title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<!-- UIkit CSS -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.35/css/uikit.min.css" />
|
||||
|
||||
<!-- UIkit JS -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.35/js/uikit.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/3.0.0-beta.35/js/uikit-icons.min.js"></script>
|
||||
|
||||
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||
<style>
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="uk-container uk-container-large">
|
||||
<!-- Title -->
|
||||
<h3 class="uk-text-center uk-heading-line uk-margin-small-top">
|
||||
<a href="/" class="">Ramtops Remedies and Reagents</a>
|
||||
</h3>
|
||||
|
||||
<!-- Top Bar -->
|
||||
<div class="" uk-grid>
|
||||
<div class="uk-width-1-3"> {% block left_top_bar %} {% endblock %} </div>
|
||||
<div class="uk-width-1-3 uk-text-center"> {% block center_top_bar %} {% endblock %} </div>
|
||||
<div class="uk-width-1-3">
|
||||
<a href="/shop/data" class="uk-button uk-button-primary uk-button-small uk-align-right uk-border-rounded">
|
||||
Report Stock
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Page Content -->
|
||||
{% block content %} {% endblock %}
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="uk-text-center uk-margin-large-top uk-margin-small-bottom">
|
||||
© 2017 by <a href="http://binaryatrocity.name">Ruhsbaar</a>
|
||||
|
|
||||
Created for Ramtops Remedies and Reagents
|
||||
|
|
||||
<a href="http://discworld.starturtle.net">DiscworldMUD</a>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
{% block pagescripts %} {% endblock %}
|
||||
<script>
|
||||
function change_date_strings() {
|
||||
var times = document.querySelectorAll('table > tbody > tr > td:last-child');
|
||||
for(var time of times) {
|
||||
var local = new Date(time.textContent + 'Z');
|
||||
|
||||
var options = {
|
||||
weekday: 'short',
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
timeZoneName: 'short'
|
||||
};
|
||||
time.innerText = local.toLocaleString('en-US', options);
|
||||
}
|
||||
}
|
||||
|
||||
/* On Page Ready */
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
change_date_strings();
|
||||
});
|
||||
</script>
|
||||
</html>
|
205
discworld/templates/shop_dashboard.html
Normal file
205
discworld/templates/shop_dashboard.html
Normal file
@ -0,0 +1,205 @@
|
||||
{% extends "layout.html" %}
|
||||
|
||||
{% block left_top_bar %}
|
||||
<button class="uk-button uk-button-default uk-button-small uk-border-rounded"
|
||||
uk-toggle="target: #charts-container">Show/Hide Charts</button>
|
||||
{% endblock %}
|
||||
|
||||
{% block center_top_bar %}
|
||||
Total Funds Earned:
|
||||
<span id="total_funds_earned" class="uk-text-primary"></span>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="charts-container" uk-grid hidden>
|
||||
<div class="uk-width-1-2">
|
||||
<canvas id="sales-chart"></canvas>
|
||||
</div>
|
||||
<div class="uk-width-1-2">
|
||||
<canvas id="stock-chart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<table id="products" class="uk-table uk-table-small uk-table-striped uk-table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="uk-table-expand">Product Name</th>
|
||||
<th>Current Stock</th>
|
||||
<th>Total Stock</th>
|
||||
<th>Total Sold</th>
|
||||
<th>Total Earned</th>
|
||||
<th>Latest Price</th>
|
||||
<th>Last Update</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for product in products %}
|
||||
{% if product.latest_entry != None %}
|
||||
<tr>
|
||||
<td class="uk-table-link">
|
||||
<a href="/shop/product/{{ product.id }}">
|
||||
{{ product.name }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ product.latest_entry.stock }}</td>
|
||||
<td>{{ product.total_stocked }}</td>
|
||||
<td>{{ product.total_sold }}</td>
|
||||
<td>A${{ product.total_earned }}</td>
|
||||
<td>
|
||||
<span title="{{ product.latest_entry.raw_price}}">
|
||||
A${{ product.latest_entry.price }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="uk-text-nowrap">{{ product.latest_entry.date.isoformat() }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
|
||||
{% block pagescripts %}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.js"></script>
|
||||
<script>
|
||||
function highlight_empty_stock() {
|
||||
var rows = document.querySelectorAll('table#products > tbody > tr > td:nth-child(2)');
|
||||
for(var row of rows) {
|
||||
if(parseInt(row.textContent) == 0) {
|
||||
row.parentElement.classList.add('uk-text-danger');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function calculate_total_earnings() {
|
||||
var total_earnings = 0.00;
|
||||
var earnings = document.querySelectorAll('table#products > tbody > tr > td:nth-child(5)');
|
||||
for(var earning of earnings) {
|
||||
total_earnings += parseFloat(earning.textContent.substring(2))
|
||||
}
|
||||
document.getElementById('total_funds_earned').textContent = "A$" + total_earnings.toFixed(2);
|
||||
}
|
||||
|
||||
/* Chart.js */
|
||||
var item_labels = [
|
||||
{% for product in products %}
|
||||
"{{ product.name }}",
|
||||
{% endfor %}
|
||||
];
|
||||
var sale_data = [
|
||||
{% for product in products %}
|
||||
{{ product.total_sold }},
|
||||
{% endfor %}
|
||||
];
|
||||
var earnings_data = [
|
||||
{% for product in products %}
|
||||
{{ product.total_earned }},
|
||||
{% endfor %}
|
||||
];
|
||||
var total_stock_data = [
|
||||
{% for product in products %}
|
||||
{{ product.total_stocked }},
|
||||
{% endfor %}
|
||||
];
|
||||
var current_stock_data = [
|
||||
{% for product in products %}
|
||||
{{ product.latest_entry.stock }},
|
||||
{% endfor %}
|
||||
];
|
||||
|
||||
function init_sales_chart(labels, sold_data, earned_data) {
|
||||
var ctx = document.getElementById("sales-chart").getContext('2d');
|
||||
var chart = new Chart(ctx, {
|
||||
type: 'horizontalBar',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [
|
||||
{
|
||||
label: 'Sold',
|
||||
backgroundColor: 'rgb(93, 173, 226)',
|
||||
borderColor: 'rgb(89, 131, 227)',
|
||||
data: sold_data,
|
||||
xAxisID: 'quantity'
|
||||
},
|
||||
{
|
||||
label: 'Earned',
|
||||
backgroundColor: 'rgb(173, 93, 226)',
|
||||
borderColor: 'rgb(131, 89, 227)',
|
||||
data: earned_data,
|
||||
xAxisID: 'currency'
|
||||
}
|
||||
],
|
||||
},
|
||||
options: {
|
||||
elements: { rectangle: { borderWidth: 2, } },
|
||||
responsive: true,
|
||||
legend: { position: 'bottom', },
|
||||
title: { display: true, text: 'Sales & Earnings'},
|
||||
scales: {
|
||||
xAxes: [
|
||||
{
|
||||
id: 'quantity',
|
||||
type: 'linear',
|
||||
position: 'top'
|
||||
},
|
||||
{
|
||||
id: 'currency',
|
||||
type: 'linear',
|
||||
position: 'bottom'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function init_stock_chart(labels, total_data, current_data) {
|
||||
var ctx = document.getElementById("stock-chart").getContext('2d');
|
||||
var chart = new Chart(ctx, {
|
||||
type: 'horizontalBar',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [
|
||||
{
|
||||
label: 'Total',
|
||||
backgroundColor: 'rgb(54, 84, 126)',
|
||||
data: total_data,
|
||||
xAxisID: 'quantity'
|
||||
},
|
||||
{
|
||||
label: 'Current',
|
||||
backgroundColor: 'rgb(73, 133, 226)',
|
||||
data: current_data,
|
||||
xAxisID: 'big_quantity'
|
||||
}
|
||||
],
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
legend: { position: 'bottom', },
|
||||
title: { display: true, text: 'Total/Current Stock'},
|
||||
scales: {
|
||||
xAxes: [
|
||||
{
|
||||
id: 'quantity',
|
||||
type: 'linear',
|
||||
position: 'top'
|
||||
},
|
||||
{
|
||||
id: 'big_quantity',
|
||||
type: 'linear',
|
||||
position: 'bottom'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* On Page Ready */
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
highlight_empty_stock();
|
||||
calculate_total_earnings();
|
||||
init_sales_chart(item_labels, sale_data, earnings_data);
|
||||
init_stock_chart(item_labels, total_stock_data, current_stock_data);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
17
discworld/templates/shop_dataentry.html
Normal file
17
discworld/templates/shop_dataentry.html
Normal file
@ -0,0 +1,17 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block content %}
|
||||
<form id="mission-form" action="{{ url_for('shop_dataentry') }}" method="post"
|
||||
class="uk-margin-large-left">
|
||||
<label for="mission-data">Enter shops "Mission Item" output:</label> <br/>
|
||||
<textarea class="uk-textarea uk-form-small uk-form-width-large uk-margin-medium-bottom"
|
||||
autofocus=true rows=15 name="mission-data"></textarea>
|
||||
|
||||
<br/>
|
||||
<label for="password">Password</label>
|
||||
<input type="password" name="password"
|
||||
class="uk-input uk-form-small uk-form-width-small uk-margin-medium-left"/>
|
||||
<br/>
|
||||
<input type="submit" value="Parse Entries"
|
||||
class="uk-button uk-button-primary uk-margin-small-top uk-border-rounded"/>
|
||||
</form>
|
||||
{% endblock %}
|
119
discworld/templates/shop_product_entries.html
Normal file
119
discworld/templates/shop_product_entries.html
Normal file
@ -0,0 +1,119 @@
|
||||
{% extends "layout.html" %}
|
||||
|
||||
{% block left_top_bar %}
|
||||
<button class="uk-button uk-button-default uk-button-small uk-border-rounded"
|
||||
uk-toggle="target: #charts-container">Show/Hide Charts</button>
|
||||
{% endblock %}
|
||||
|
||||
{% block center_top_bar %}
|
||||
<h4 class="uk-text-center">{{ product.name }}</h4>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="charts-container" uk-grid hidden>
|
||||
<div class="uk-width-1-1">
|
||||
<canvas id="time-chart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<table id="products" class="uk-table uk-table-small uk-table-striped uk-table-hover uk-margin-large-left">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Stock</th>
|
||||
<th>Price (Raw)</th>
|
||||
<th>Price (Cvt)</th>
|
||||
<th>Update</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in product.entries %}
|
||||
<tr>
|
||||
<td>{{ entry.stock }}</td>
|
||||
<td>{{ entry.raw_price }}</td>
|
||||
<td>A${{ entry.price }}</td>
|
||||
<td>{{ entry.date.isoformat() }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<caption class="uk-margin-small-bottom">{{ product.entries | length }} total entries.</caption>
|
||||
</table>
|
||||
{% endblock %}
|
||||
|
||||
{% block pagescripts %}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.3/moment.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.js"></script>
|
||||
<script>
|
||||
/* Chart.js */
|
||||
var item_labels = [
|
||||
"{{ product.name }}",
|
||||
];
|
||||
var time_data = [
|
||||
{% for entry in product.entries %}
|
||||
{x: "{{ entry.date.isoformat() }}", y: {{ entry.stock }}},
|
||||
{% endfor %}
|
||||
];
|
||||
|
||||
function init_time_chart(labels, timedata) {
|
||||
var ctx = document.getElementById("time-chart").getContext('2d');
|
||||
var chart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [
|
||||
{
|
||||
label: 'Entries',
|
||||
backgroundColor: 'rgb(93, 173, 226)',
|
||||
borderColor: 'rgb(89, 131, 227)',
|
||||
fill: false,
|
||||
data: timedata,
|
||||
}
|
||||
],
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
title: { display: true, text: 'Entries'},
|
||||
scales: {
|
||||
xAxes: [
|
||||
{
|
||||
type: 'time',
|
||||
distribution: 'series',
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: 'Date'
|
||||
},
|
||||
time: {
|
||||
unit: 'day',
|
||||
unitStepSize: 1,
|
||||
displayFormats: {
|
||||
day: 'MMM D YYYY h:mm a'
|
||||
}
|
||||
},
|
||||
ticks: {
|
||||
major: {
|
||||
fontStyle: "bold",
|
||||
fontColor: "#FF0000"
|
||||
},
|
||||
source: 'data'
|
||||
}
|
||||
}
|
||||
],
|
||||
yAxes: [
|
||||
{
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
display: true,
|
||||
labelString: 'Stock'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* On Page Ready */
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
init_time_chart(item_labels, time_data);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
36
discworld/views.py
Normal file
36
discworld/views.py
Normal file
@ -0,0 +1,36 @@
|
||||
import re
|
||||
|
||||
from flask import (
|
||||
request,
|
||||
url_for,
|
||||
redirect,
|
||||
render_template
|
||||
)
|
||||
|
||||
from . import (
|
||||
db,
|
||||
app,
|
||||
models,
|
||||
shop
|
||||
)
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def shop_dashboard():
|
||||
return render_template('shop_dashboard.html',
|
||||
products=models.ShopProduct.query.all())
|
||||
|
||||
@app.route('/shop/data', methods=['POST','GET'])
|
||||
def shop_dataentry():
|
||||
if request.method == 'POST':
|
||||
if request.form.get('password') == 'r3m3di3s' and request.form.get('mission-data'):
|
||||
data = request.form['mission-data']
|
||||
clean_data = data.replace('\r\n', '\n')
|
||||
shop.parse_shop_output(clean_data)
|
||||
return redirect(url_for('shop_dashboard'))
|
||||
return render_template('shop_dataentry.html')
|
||||
|
||||
@app.route('/shop/product/<int:product_id>')
|
||||
def shop_product_entries(product_id):
|
||||
product = models.ShopProduct.query.filter_by(id=product_id).first_or_404()
|
||||
return render_template('shop_product_entries.html', product=product)
|
11
uwsgi.ini
Normal file
11
uwsgi.ini
Normal file
@ -0,0 +1,11 @@
|
||||
[uwsgi]
|
||||
module = wsgi:application
|
||||
|
||||
master = true
|
||||
processes = 4
|
||||
|
||||
socket = discworld.sock
|
||||
chmod-scoket = 660
|
||||
vacuum = true
|
||||
|
||||
die-on-term = true
|
Loading…
x
Reference in New Issue
Block a user