Compare commits

...

16 Commits
v2.0.0 ... main

Author SHA1 Message Date
aronwk-aaron
823ec2008f util command to get mission/ach awards that aren't in honor accollade 2024-05-26 22:49:32 -05:00
aronwk-aaron
f3e2254330 fix messed up table 2024-03-11 12:33:33 -05:00
Aaron Kimbrell
d6b0a91e4d
Merge pull request #91 from DarkflameUniverse/fix/libsass
Reintroduce libsass
2024-01-10 18:06:41 -06:00
Xiphoseer
d698e650ad feat: upgrade to python3.11; debian 12 (bookworm) 2024-01-11 01:02:31 +01:00
Xiphoseer
cde585fad8 fix: re-add libsass 2024-01-11 01:01:33 +01:00
Aaron Kimbrell
8b70f259c0
Merge pull request #88 from DarkflameUniverse/fix/requirements
fix: remove transitive deps from requirements.txt
2024-01-07 16:22:06 -06:00
Xiphoseer
de50bc7278 fix: remove transitive deps from requirements.txt 2024-01-07 22:12:32 +01:00
Aaron Kimbrell
2e4bd04d09
Merge pull request #87 from DarkflameUniverse/fix/empty-play-key
fix: don't break on empty play key
2024-01-07 15:00:26 -06:00
Xiphoseer
ccc793a129 fix: don't break on empty play key 2024-01-07 21:56:56 +01:00
Aaron Kimbrell
69823be5c8
Merge pull request #86 from DarkflameUniverse/fix/migrate-env
Fix DB connection URL in env.py
2024-01-07 13:57:38 -06:00
Daniel Seiler
3027534b16
Fix DB connection URL in env.py
Previously, this line was returning `***` as the DB password
2024-01-07 20:50:46 +01:00
aronwk-aaron
09096fe1c4 bump action versions 2023-12-31 01:57:16 -06:00
Aaron Kimbrell
9bfa55ac8e
Merge pull request #81 from Ramen2X/main
fix readme typo (exmaple -> example)
2023-12-18 10:11:00 -06:00
Ramen2X
1dee96c04f
fix typo (exmaple -> example) 2023-12-17 16:42:01 -05:00
aronwk-aaron
d005b497e6 compress the reports :D 2023-11-18 05:46:50 -06:00
aronwk-aaron
259efc81fd fix: don't show count of 1 on items
fix: tooltips on lazy loaded items
2023-11-18 01:13:05 -06:00
15 changed files with 247 additions and 93 deletions

View File

@ -26,7 +26,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Log in to the Container registry - name: Log in to the Container registry
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 uses: docker/login-action@v3
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
@ -34,7 +34,7 @@ jobs:
- name: Extract metadata (tags, labels) for Docker - name: Extract metadata (tags, labels) for Docker
id: meta id: meta
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 uses: docker/metadata-action@v5
with: with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# generate Docker tags based on the following events/attributes # generate Docker tags based on the following events/attributes
@ -46,7 +46,7 @@ jobs:
type=semver,pattern={{major}} type=semver,pattern={{major}}
- name: Build and push Docker image - name: Build and push Docker image
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc uses: docker/build-push-action@v5
with: with:
context: . context: .
push: ${{ github.event_name != 'pull_request' }} push: ${{ github.event_name != 'pull_request' }}

View File

@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
FROM python:3.8-slim-buster FROM python:3.11-slim-bookworm
RUN apt update RUN apt update
RUN apt -y install zip RUN apt -y install zip

View File

@ -98,7 +98,7 @@ docker run -d \
### Environmental Variables ### Environmental Variables
Please Reference `app/settings_exmaple.py` to see all the variables Please Reference `app/settings_example.py` to see all the variables
* Required: * Required:
* APP_SECRET_KEY (Must be provided) * APP_SECRET_KEY (Must be provided)

View File

@ -19,7 +19,8 @@ from app.commands import (
gen_image_cache, gen_image_cache,
gen_model_cache, gen_model_cache,
fix_clone_ids, fix_clone_ids,
remove_buffs remove_buffs,
find_missing_commendation_items
) )
from app.models import Account, AccountInvitation, AuditLog from app.models import Account, AccountInvitation, AuditLog
@ -96,6 +97,7 @@ def create_app():
app.cli.add_command(gen_model_cache) app.cli.add_command(gen_model_cache)
app.cli.add_command(fix_clone_ids) app.cli.add_command(fix_clone_ids)
app.cli.add_command(remove_buffs) app.cli.add_command(remove_buffs)
app.cli.add_command(find_missing_commendation_items)
register_logging(app) register_logging(app)
register_settings(app) register_settings(app)

View File

@ -252,6 +252,9 @@ def get():
# Delete # Delete
# </a> # </a>
if not current_app.config["USER_ENABLE_EMAIL"]:
account["2"] = '''N/A'''
if account["4"]: if account["4"]:
account["4"] = '''<h2 class="far fa-times-circle text-danger"></h2>''' account["4"] = '''<h2 class="far fa-times-circle text-danger"></h2>'''
else: else:
@ -267,20 +270,11 @@ def get():
else: else:
account["6"] = '''<h2 class="far fa-check-square text-success"></h2>''' account["6"] = '''<h2 class="far fa-check-square text-success"></h2>'''
if current_app.config["USER_ENABLE_EMAIL"]: if not current_app.config["USER_ENABLE_EMAIL"]:
if account["8"]: account["8"] = '''<h2 class="far fa-times-circle text-muted"></h2>'''
account["8"] = '''<h2 class="far fa-check-square text-success"></h2>''' elif account["8"]:
else: account["8"] = '''<h2 class="far fa-check-square text-success"></h2>'''
account["8"] = '''<h2 class="far fa-times-circle text-danger"></h2>'''
else: else:
# shift columns to fill in gap of 2 account["8"] = '''<h2 class="far fa-times-circle text-danger"></h2>'''
account["2"] = account["3"]
account["3"] = account["4"]
account["4"] = account["5"]
account["5"] = account["6"]
account["6"] = account["7"]
# remove last two columns
del account["7"]
del account["8"]
return data return data

View File

@ -4,7 +4,7 @@ import random
import string import string
import datetime import datetime
from flask_user import current_app from flask_user import current_app
from app import db from app import db, luclient
from app.models import Account, PlayKey, CharacterInfo, Property, PropertyContent, UGC, Mail, CharacterXML from app.models import Account, PlayKey, CharacterInfo, Property, PropertyContent, UGC, Mail, CharacterXML
import pathlib import pathlib
import zlib import zlib
@ -281,3 +281,41 @@ def find_or_create_account(name, email, password, gm_level=9):
db.session.add(play_key) db.session.add(play_key)
db.session.commit() db.session.commit()
return # account return # account
@click.command("find_missing_commendation_items")
@with_appcontext
def find_missing_commendation_items():
data = dict()
lots = set()
reward_items = luclient.query_cdclient("Select reward_item1 from Missions;")
for reward_item in reward_items:
lots.add(reward_item[0])
reward_items = luclient.query_cdclient("Select reward_item2 from Missions;")
for reward_item in reward_items:
lots.add(reward_item[0])
reward_items = luclient.query_cdclient("Select reward_item3 from Missions;")
for reward_item in reward_items:
lots.add(reward_item[0])
reward_items = luclient.query_cdclient("Select reward_item4 from Missions;")
for reward_item in reward_items:
lots.add(reward_item[0])
lots.remove(0)
lots.remove(-1)
for lot in lots:
itemcompid = luclient.query_cdclient(
"Select component_id from ComponentsRegistry where component_type = 11 and id = ?;",
[lot],
one=True
)[0]
itemcomp = luclient.query_cdclient(
"Select commendationLOT, commendationCost from ItemComponent where id = ?;",
[itemcompid],
one=True
)
if itemcomp[0] is None or itemcomp[1] is None:
data[lot] = {"name": luclient.get_lot_name(lot)}
print(data)

View File

@ -22,17 +22,17 @@ from app.models import PlayKey
def validate_play_key(form, field): def validate_play_key(form, field):
"""Validates a field for a valid phone number """Validates a field for a valid play kyey
Args: Args:
form: REQUIRED, the field's parent form form: REQUIRED, the field's parent form
field: REQUIRED, the field with data field: REQUIRED, the field with data
Returns: Returns:
None, raises ValidationError if failed None, raises ValidationError if failed
""" """
# jank to get the fireign key that we need back into the field # jank to get the foreign key that we need back into the field
if current_app.config["REQUIRE_PLAY_KEY"]: if current_app.config["REQUIRE_PLAY_KEY"]:
field.data = PlayKey.key_is_valid(key_string=field.data) field.data = PlayKey.key_is_valid(key_string=field.data)
return return True
class CustomRecaptcha(Recaptcha): class CustomRecaptcha(Recaptcha):
def __call__(self, form, field): def __call__(self, form, field):
@ -49,10 +49,7 @@ class CustomUserManager(UserManager):
class CustomRegisterForm(RegisterForm): class CustomRegisterForm(RegisterForm):
play_key_id = StringField( play_key_id = StringField(
'Play Key', 'Play Key',
validators=[ validators=[validate_play_key]
Optional(),
validate_play_key,
]
) )
recaptcha = RecaptchaField( recaptcha = RecaptchaField(
validators=[CustomRecaptcha()] validators=[CustomRecaptcha()]

View File

@ -7,7 +7,6 @@ import logging
from flask_sqlalchemy.query import Query from flask_sqlalchemy.query import Query
from sqlalchemy.dialects import mysql from sqlalchemy.dialects import mysql
from sqlalchemy.exc import OperationalError, StatementError from sqlalchemy.exc import OperationalError, StatementError
from sqlalchemy.types import JSON
from time import sleep from time import sleep
import random import random
import string import string
@ -1018,7 +1017,7 @@ class Reports(db.Model):
__tablename__ = 'reports' __tablename__ = 'reports'
data = db.Column( data = db.Column(
JSON(), mysql.MEDIUMBLOB(),
nullable=False nullable=False
) )

View File

@ -4,8 +4,7 @@ from app.models import CharacterInfo, Account, CharacterXML, Reports
from app.luclient import get_lot_name from app.luclient import get_lot_name
from app import gm_level, scheduler from app import gm_level, scheduler
from sqlalchemy.orm import load_only from sqlalchemy.orm import load_only
import datetime import xmltodict, gzip, json, datetime
import xmltodict
reports_blueprint = Blueprint('reports', __name__) reports_blueprint = Blueprint('reports', __name__)
@ -44,6 +43,8 @@ def index():
@gm_level(3) @gm_level(3)
def items_by_date(date): def items_by_date(date):
data = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "items").first().data data = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "items").first().data
data = gzip.decompress(data)
data = json.loads(data.decode('utf-8'))
return render_template('reports/items/by_date.html.j2', data=data, date=date) return render_template('reports/items/by_date.html.j2', data=data, date=date)
@ -62,6 +63,8 @@ def items_graph(start, end):
datasets = [] datasets = []
# get stuff ready # get stuff ready
for entry in entries: for entry in entries:
entry.data = gzip.decompress(entry.data)
entry.data = json.loads(entry.data.decode('utf-8'))
labels.append(entry.date.strftime("%m/%d/%Y")) labels.append(entry.date.strftime("%m/%d/%Y"))
for key in entry.data: for key in entry.data:
items[key] = get_lot_name(key) items[key] = get_lot_name(key)
@ -104,6 +107,8 @@ def items_graph(start, end):
@gm_level(3) @gm_level(3)
def currency_by_date(date): def currency_by_date(date):
data = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "currency").first().data data = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "currency").first().data
data = gzip.decompress(data)
data = json.loads(data.decode('utf-8'))
return render_template('reports/currency/by_date.html.j2', data=data, date=date) return render_template('reports/currency/by_date.html.j2', data=data, date=date)
@ -121,6 +126,8 @@ def currency_graph(start, end):
datasets = [] datasets = []
# get stuff ready # get stuff ready
for entry in entries: for entry in entries:
entry.data = gzip.decompress(entry.data)
entry.data = json.loads(entry.data.decode('utf-8'))
labels.append(entry.date.strftime("%m/%d/%Y")) labels.append(entry.date.strftime("%m/%d/%Y"))
for character in characters: for character in characters:
data = [] data = []
@ -155,6 +162,8 @@ def currency_graph(start, end):
@gm_level(3) @gm_level(3)
def uscore_by_date(date): def uscore_by_date(date):
data = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "uscore").first().data data = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "uscore").first().data
data = gzip.decompress(data)
data = json.loads(data.decode('utf-8'))
return render_template('reports/uscore/by_date.html.j2', data=data, date=date) return render_template('reports/uscore/by_date.html.j2', data=data, date=date)
@ -172,6 +181,8 @@ def uscore_graph(start, end):
datasets = [] datasets = []
# get stuff ready # get stuff ready
for entry in entries: for entry in entries:
entry.data = gzip.decompress(entry.data)
entry.data = json.loads(entry.data.decode('utf-8'))
labels.append(entry.date.strftime("%m/%d/%Y")) labels.append(entry.date.strftime("%m/%d/%Y"))
for character in characters: for character in characters:
data = [] data = []
@ -260,7 +271,7 @@ def gen_item_report():
current_app.logger.error(f"REPORT::ITEMS - {e}") current_app.logger.error(f"REPORT::ITEMS - {e}")
new_report = Reports( new_report = Reports(
data=report_data, data=gzip.compress(json.dumps(report_data).encode('utf-8')),
report_type="items", report_type="items",
date=date date=date
) )
@ -308,7 +319,7 @@ def gen_currency_report():
current_app.logger.error(f"REPORT::CURRENCY - {e}") current_app.logger.error(f"REPORT::CURRENCY - {e}")
new_report = Reports( new_report = Reports(
data=report_data, data=gzip.compress(json.dumps(report_data).encode('utf-8')),
report_type="currency", report_type="currency",
date=date date=date
) )
@ -356,7 +367,7 @@ def gen_uscore_report():
current_app.logger.error(f"REPORT::U-SCORE - {e}") current_app.logger.error(f"REPORT::U-SCORE - {e}")
new_report = Reports( new_report = Reports(
data=report_data, data=gzip.compress(json.dumps(report_data).encode('utf-8')),
report_type="uscore", report_type="uscore",
date=date date=date
) )

View File

@ -14,17 +14,13 @@
<tr> <tr>
<th>Actions</th> <th>Actions</th>
<th>Name</th> <th>Name</th>
{% if config.USER_ENABLE_EMAIL %} <th>Email</th>
<th>Email</th>
{% endif %}
<th>GM Level</th> <th>GM Level</th>
<th>Locked</th> <th>Locked</th>
<th>Banned</th> <th>Banned</th>
<th>Muted</th> <th>Muted</th>
<th>Registered</th> <th>Registered</th>
{% if config.USER_ENABLE_EMAIL %} <th>Email Confirmed</th>
<th>Email Confirmed</th>
{% endif %}
</tr> </tr>
</thead> </thead>
<tbody></tbody> <tbody></tbody>

View File

@ -96,7 +96,7 @@
}) })
function setInnerHTML(elm, html) { function setInnerHTML(elm, html) {
elm.innerHTML = html; elm.innerHTML = html;
$("body").tooltip({ selector: '[data-toggle=tooltip]' });
Array.from(elm.querySelectorAll("script")) Array.from(elm.querySelectorAll("script"))
.forEach( oldScriptEl => { .forEach( oldScriptEl => {
const newScriptEl = document.createElement("script"); const newScriptEl = document.createElement("script");
@ -109,6 +109,7 @@
newScriptEl.appendChild(scriptText); newScriptEl.appendChild(scriptText);
oldScriptEl.parentNode.replaceChild(newScriptEl, oldScriptEl); oldScriptEl.parentNode.replaceChild(newScriptEl, oldScriptEl);
}); });
} }
</script> </script>

View File

@ -17,7 +17,7 @@
<span class="inventory-count text-bold"> <span class="inventory-count text-bold">
{%if inv_item.attr_c|int > 999 %} {%if inv_item.attr_c|int > 999 %}
+999 +999
{% else %} {% elif inv_item.attr_c|int > 1 %}
{{ inv_item.attr_c }} {{ inv_item.attr_c }}
{% endif %} {% endif %}
</span> </span>

View File

@ -22,8 +22,8 @@ logger = logging.getLogger('alembic.env')
# target_metadata = mymodel.Base.metadata # target_metadata = mymodel.Base.metadata
config.set_main_option( config.set_main_option(
'sqlalchemy.url', 'sqlalchemy.url',
str(current_app.extensions['migrate'].db.get_engine().url).replace( current_app.config.get('SQLALCHEMY_DATABASE_URI')
'%', '%%')) )
target_metadata = current_app.extensions['migrate'].db.metadata target_metadata = current_app.extensions['migrate'].db.metadata
# other values from the config, defined by the needs of env.py, # other values from the config, defined by the needs of env.py,

View File

@ -0,0 +1,164 @@
"""compressss reports
Revision ID: 1164e037907f
Revises: a6e42ef03da7
Create Date: 2023-11-18 01:38:00.127472
"""
from alembic import op
from sqlalchemy.orm.session import Session
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
from sqlalchemy.types import JSON
from sqlalchemy.ext.declarative import declarative_base
import gzip
import json
Base = declarative_base()
# revision identifiers, used by Alembic.
revision = '1164e037907f'
down_revision = 'a6e42ef03da7'
branch_labels = None
depends_on = None
class ReportsUpgradeNew(Base):
__tablename__ = 'reports'
__table_args__ = {'extend_existing': True}
data = sa.Column(
mysql.MEDIUMBLOB(),
nullable=False
)
report_type = sa.Column(
sa.VARCHAR(35),
nullable=False,
primary_key=True,
autoincrement=False
)
date = sa.Column(
sa.Date(),
primary_key=True,
autoincrement=False
)
def save(self):
sa.session.add(self)
sa.session.commit()
sa.session.refresh(self)
class ReportsUpgradeOld(Base):
__tablename__ = 'reports_old'
__table_args__ = {'extend_existing': True}
data = sa.Column(
JSON(),
nullable=False
)
report_type = sa.Column(
sa.VARCHAR(35),
nullable=False,
primary_key=True,
autoincrement=False
)
date = sa.Column(
sa.Date(),
primary_key=True,
autoincrement=False
)
class ReportsDowngradeOld(Base):
__tablename__ = 'reports'
__table_args__ = {'extend_existing': True}
data = sa.Column(
mysql.MEDIUMBLOB(),
nullable=False
)
report_type = sa.Column(
sa.VARCHAR(35),
nullable=False,
primary_key=True,
autoincrement=False
)
date = sa.Column(
sa.Date(),
primary_key=True,
autoincrement=False
)
def save(self):
sa.session.add(self)
sa.session.commit()
sa.session.refresh(self)
class ReportsDowngradeNew(Base):
__tablename__ = 'reports_old'
__table_args__ = {'extend_existing': True}
data = sa.Column(
JSON(),
nullable=False
)
report_type = sa.Column(
sa.VARCHAR(35),
nullable=False,
primary_key=True,
autoincrement=False
)
date = sa.Column(
sa.Date(),
primary_key=True,
autoincrement=False
)
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.rename_table('reports', 'reports_old')
bind = op.get_bind()
session = Session(bind=bind)
reports = session.query(ReportsUpgradeOld)
op.create_table('reports',
sa.Column('data', mysql.MEDIUMBLOB(), nullable=False),
sa.Column('report_type', sa.VARCHAR(length=35), autoincrement=False, nullable=False),
sa.Column('date', sa.Date(), autoincrement=False, nullable=False),
sa.PrimaryKeyConstraint('report_type', 'date')
)
# insert records
new_reports = []
# insert records
for report in reports:
new_reports.append({
"data":gzip.compress(json.dumps(report.data).encode('utf-8')),
"report_type":report.report_type,
"date":report.date
})
op.bulk_insert(ReportsUpgradeNew.__table__, new_reports)
op.drop_table('reports_old')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('reports')
op.create_table('reports',
sa.Column('data', JSON(), nullable=False),
sa.Column('report_type', sa.VARCHAR(length=35), autoincrement=False, nullable=False),
sa.Column('date', sa.Date(), autoincrement=False, nullable=False),
sa.PrimaryKeyConstraint('report_type', 'date')
)
# ### end Alembic commands ###

View File

@ -1,68 +1,20 @@
alembic==1.7.5 alembic==1.7.5
APScheduler==3.8.1
astroid==2.9.1
autopep8==1.6.0 autopep8==1.6.0
backports.zoneinfo==0.2.1
bcrypt==3.2.0
blinker==1.7.0
cffi==1.14.6
click==8.1.7
colorama==0.4.4
cryptography==36.0.0
dnspython==2.1.0
dominate==2.6.0
email-validator==1.1.3 email-validator==1.1.3
Flask==3.0.0 Flask==3.0.0
Flask-APScheduler==1.12.3 Flask-APScheduler==1.12.3
Flask-Assets==2.1.0 Flask-Assets==2.1.0
Flask-Login==0.6.3
Flask-Mail==0.9.1
flask-marshmallow==0.15.0
Flask-Migrate==3.1.0 Flask-Migrate==3.1.0
Flask-SQLAlchemy==3.1.1 Flask-SQLAlchemy==3.1.1
Flask-User==1.0.2.2 Flask-User==1.0.2.2
Flask-WTF==1.2.1 Flask-WTF==1.2.1
greenlet==1.1.0
gunicorn==21.2.0 gunicorn==21.2.0
idna==3.3
importlib-metadata==6.8.0
importlib-resources==6.1.1
isort==5.10.1
itsdangerous==2.1.2
Jinja2==3.1.2
lazy-object-proxy==1.7.1
libsass==0.21.0 libsass==0.21.0
Mako==1.2.2
MarkupSafe==2.1.3
marshmallow==3.14.1
marshmallow-sqlalchemy==0.26.1
mccabe==0.6.1
packaging==23.2
passlib==1.7.4
platformdirs==2.4.1
pycodestyle==2.8.0
pycparser==2.20
pydocstyle==6.1.1
pyflakes==2.4.0
pylama==8.3.3
pylint==2.12.2
PyMySQL==1.0.2 PyMySQL==1.0.2
python-dateutil==2.8.2
pytz==2021.3
pytz-deprecation-shim==0.1.0.post0
six==1.16.0
snowballstemmer==2.2.0
SQLAlchemy==2.0.23 SQLAlchemy==2.0.23
sqlalchemy-datatables==2.0.1 sqlalchemy-datatables==2.0.1
toml==0.10.2
typing_extensions==4.8.0
tzdata==2021.5
tzlocal==4.1
visitor==0.1.3
Wand==0.6.7 Wand==0.6.7
webassets==2.0 webassets==2.0
Werkzeug==3.0.1 Werkzeug==3.0.1
wrapt==1.13.3
WTForms==3.0.0 WTForms==3.0.0
xmltodict==0.12.0 xmltodict==0.12.0
zipp==3.17.0