commit 7825abf11c0758ada072a6cb714acc53cd075ba8 Author: Brandon Cornejo Date: Mon May 28 22:43:59 2018 -0500 Initial commit diff --git a/config.py b/config.py new file mode 100644 index 0000000..8613f9d --- /dev/null +++ b/config.py @@ -0,0 +1,3 @@ +DEBUG = False +SQLALCHEMY_TRACK_MODIFICATIONS = False +SQLALCHEMY_DATABASE_URI = 'sqlite:///../discworld.db' diff --git a/discworld.db b/discworld.db new file mode 100644 index 0000000..c33e3a8 Binary files /dev/null and b/discworld.db differ diff --git a/discworld/__init__.py b/discworld/__init__.py new file mode 100644 index 0000000..bdabc8a --- /dev/null +++ b/discworld/__init__.py @@ -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 diff --git a/discworld/models.py b/discworld/models.py new file mode 100644 index 0000000..ebdf3cc --- /dev/null +++ b/discworld/models.py @@ -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 ''.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 ''.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) diff --git a/discworld/shop.py b/discworld/shop.py new file mode 100644 index 0000000..5a47584 --- /dev/null +++ b/discworld/shop.py @@ -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() diff --git a/discworld/static/favicon.ico b/discworld/static/favicon.ico new file mode 100644 index 0000000..7647521 Binary files /dev/null and b/discworld/static/favicon.ico differ diff --git a/discworld/templates/layout.html b/discworld/templates/layout.html new file mode 100644 index 0000000..1f979df --- /dev/null +++ b/discworld/templates/layout.html @@ -0,0 +1,76 @@ + + + + Ramtops Remedies and Reagents + + + + + + + + + + + + + + + +
+ +

+ Ramtops Remedies and Reagents +

+ + +
+
{% block left_top_bar %} {% endblock %}
+
{% block center_top_bar %} {% endblock %}
+ +
+ + + {% block content %} {% endblock %} + + +
+ © 2017 by Ruhsbaar +    |    + Created for Ramtops Remedies and Reagents +    |    + DiscworldMUD +
+
+ + {% block pagescripts %} {% endblock %} + + diff --git a/discworld/templates/shop_dashboard.html b/discworld/templates/shop_dashboard.html new file mode 100644 index 0000000..4d54793 --- /dev/null +++ b/discworld/templates/shop_dashboard.html @@ -0,0 +1,205 @@ +{% extends "layout.html" %} + +{% block left_top_bar %} + +{% endblock %} + +{% block center_top_bar %} + Total Funds Earned: + +{% endblock %} + +{% block content %} + + + + + + + + + + + + + + + {% for product in products %} + {% if product.latest_entry != None %} + + + + + + + + + + {% endif %} + {% endfor %} + +
Product NameCurrent StockTotal StockTotal SoldTotal EarnedLatest PriceLast Update
{{ product.latest_entry.stock }}{{ product.total_stocked }}{{ product.total_sold }}A${{ product.total_earned }} + + A${{ product.latest_entry.price }} + + {{ product.latest_entry.date.isoformat() }}
+{% endblock %} + +{% block pagescripts %} + + +{% endblock %} diff --git a/discworld/templates/shop_dataentry.html b/discworld/templates/shop_dataentry.html new file mode 100644 index 0000000..3483531 --- /dev/null +++ b/discworld/templates/shop_dataentry.html @@ -0,0 +1,17 @@ +{% extends "layout.html" %} +{% block content %} +
+
+ + +
+ + +
+ +
+{% endblock %} diff --git a/discworld/templates/shop_product_entries.html b/discworld/templates/shop_product_entries.html new file mode 100644 index 0000000..2d83dfa --- /dev/null +++ b/discworld/templates/shop_product_entries.html @@ -0,0 +1,119 @@ +{% extends "layout.html" %} + +{% block left_top_bar %} + +{% endblock %} + +{% block center_top_bar %} +

{{ product.name }}

+{% endblock %} + +{% block content %} + + + + + + + + + + + + {% for entry in product.entries %} + + + + + + + {% endfor %} + + +
StockPrice (Raw)Price (Cvt)Update
{{ entry.stock }}{{ entry.raw_price }}A${{ entry.price }}{{ entry.date.isoformat() }}
{{ product.entries | length }} total entries.
+{% endblock %} + +{% block pagescripts %} + + + +{% endblock %} diff --git a/discworld/views.py b/discworld/views.py new file mode 100644 index 0000000..8c84e01 --- /dev/null +++ b/discworld/views.py @@ -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/') +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) diff --git a/uwsgi.ini b/uwsgi.ini new file mode 100644 index 0000000..3507337 --- /dev/null +++ b/uwsgi.ini @@ -0,0 +1,11 @@ +[uwsgi] +module = wsgi:application + +master = true +processes = 4 + +socket = discworld.sock +chmod-scoket = 660 +vacuum = true + +die-on-term = true diff --git a/wsgi.py b/wsgi.py new file mode 100644 index 0000000..27fe4cd --- /dev/null +++ b/wsgi.py @@ -0,0 +1,4 @@ +from discworld import app as application + +if __name__ == "__main__": + application.run()