syntax/linting fixes

This commit is contained in:
Aaron Kimbre 2022-03-12 20:09:35 -06:00
parent 70742549b9
commit 5ce9ac85bc
18 changed files with 504 additions and 375 deletions

View File

@ -10,7 +10,7 @@ from app.forms import CustomUserManager
from flask_user import user_registered, current_user, user_logged_in
from flask_wtf.csrf import CSRFProtect
from flask_apscheduler import APScheduler
from app.luclient import query_cdclient, register_luclient_jinja_helpers
from app.luclient import register_luclient_jinja_helpers
from app.commands import init_db, init_accounts, load_property, gen_image_cache, gen_model_cache
from app.models import Account, AccountInvitation, AuditLog
@ -46,7 +46,6 @@ def create_app():
else:
app.logger.info(f"USERS::REGISTRATION User with ID {user.id} and name {user.username} Registered")
@user_logged_in.connect_via(app)
def _after_login_hook(sender, user, **extra):
app.logger.info(f"{user.username} Logged in")
@ -246,6 +245,7 @@ def gm_level(gm_level):
return wrapper
return decorator
def log_audit(message):
AuditLog(
account_id=current_user.id,

View File

@ -1,10 +1,8 @@
from flask import render_template, Blueprint, redirect, url_for, request, abort, current_app, flash, current_app
from flask import render_template, Blueprint, redirect, url_for, request, current_app, flash
from flask_user import login_required, current_user
import json
from datatables import ColumnDT, DataTables
import datetime
import time
from app.models import Account, AccountInvitation, db
from app.models import Account, db
from app.schemas import AccountSchema
from app import gm_level, log_audit
from app.forms import EditGMLevelForm
@ -13,6 +11,7 @@ accounts_blueprint = Blueprint('accounts', __name__)
account_schema = AccountSchema()
@accounts_blueprint.route('/', methods=['GET'])
@login_required
@gm_level(3)
@ -154,13 +153,13 @@ def get():
account["5"] = '''<h2 class="far fa-check-square text-success"></h2>'''
if account["6"]:
account["6"] = f'''<h2 class="far fa-times-circle text-danger"></h2>'''
account["6"] = '''<h2 class="far fa-times-circle text-danger"></h2>'''
else:
account["6"] = '''<h2 class="far fa-check-square text-success"></h2>'''
if current_app.config["USER_ENABLE_EMAIL"]:
if account["8"]:
account["8"] = f'''<h2 class="far fa-check-square text-success"></h2>'''
account["8"] = '''<h2 class="far fa-check-square text-success"></h2>'''
else:
account["8"] = '''<h2 class="far fa-times-circle text-danger"></h2>'''
else:
@ -175,4 +174,3 @@ def get():
del account["8"]
return data

View File

@ -1,4 +1,4 @@
from flask import render_template, Blueprint, redirect, url_for, request, abort, flash
from flask import render_template, Blueprint, redirect, url_for, request, flash
from flask_user import login_required, current_user
from app.models import db, BugReport, CharacterInfo
from datatables import ColumnDT, DataTables
@ -8,6 +8,7 @@ from app.luclient import translate_from_locale
bug_report_blueprint = Blueprint('bug_reports', __name__)
@bug_report_blueprint.route('/<status>', methods=['GET'])
@login_required
@gm_level(3)
@ -65,9 +66,9 @@ def get(status):
if status == "all":
query = db.session.query().select_from(BugReport)
elif status == "resolved":
query = db.session.query().select_from(BugReport).filter(BugReport.resolved_time != None)
query = db.session.query().select_from(BugReport).filter(BugReport.resolved_time not None)
elif status == "unresolved":
query = db.session.query().select_from(BugReport).filter(BugReport.resolved_time == None)
query = db.session.query().select_from(BugReport).filter(BugReport.resolved_time is None)
else:
raise Exception("Not a valid filter")

View File

@ -1,8 +1,7 @@
from flask import render_template, Blueprint, redirect, url_for, request, abort, flash, make_response
from flask_user import login_required, current_user
import json
from datatables import ColumnDT, DataTables
import datetime, time
import time
from app.models import CharacterInfo, CharacterXML, Account, db
from app.schemas import CharacterInfoSchema
from app.forms import RescueForm
@ -16,6 +15,7 @@ character_blueprint = Blueprint('characters', __name__)
character_schema = CharacterInfoSchema()
@character_blueprint.route('/', methods=['GET'])
@login_required
@gm_level(3)
@ -42,9 +42,12 @@ def approve_name(id, action):
elif action == "rename":
character.needs_rename = True
log_audit(f"Marked character ({character.id}){character.name} (Pending Name: {character.pending_name if character.pending_name else 'None'}) as needing Rename")
log_audit(
f"Marked character ({character.id}){character.name} \
(Pending Name: {character.pending_name if character.pending_name else 'None'}) as needing Rename")
flash(
f"Marked character {character.name} (Pending Name: {character.pending_name if character.pending_name else 'None'}) as needing Rename",
f"Marked character {character.name} \
(Pending Name: {character.pending_name if character.pending_name else 'None'}) as needing Rename",
"danger"
)
@ -86,7 +89,6 @@ def view(id):
if "i" in inv.keys() and type(inv["i"]) == list:
inv["i"] = sorted(inv["i"], key=lambda i: int(i['attr_s']))
return render_template(
'character/view.html.j2',
character_data=character_data,
@ -117,6 +119,7 @@ def view_xml(id):
response.headers.set('Content-Type', 'text/xml')
return response
@character_blueprint.route('/get_xml/<id>', methods=['GET'])
@login_required
def get_xml(id):
@ -144,6 +147,7 @@ def get_xml(id):
)
return response
@character_blueprint.route('/restrict/<bit>/<id>', methods=['GET'])
@login_required
@gm_level(3)
@ -205,6 +209,7 @@ def rescue(id):
return render_template("character/rescue.html.j2", form=form)
@character_blueprint.route('/get/<status>', methods=['GET'])
@login_required
@gm_level(3)
@ -223,9 +228,9 @@ def get(status):
if status == "all":
query = db.session.query().select_from(CharacterInfo).join(Account)
elif status == "approved":
query = db.session.query().select_from(CharacterInfo).join(Account).filter((CharacterInfo.pending_name == "") & (CharacterInfo.needs_rename == False))
query = db.session.query().select_from(CharacterInfo).join(Account).filter((CharacterInfo.pending_name == "") & (CharacterInfo.needs_rename is False))
elif status == "unapproved":
query = db.session.query().select_from(CharacterInfo).join(Account).filter((CharacterInfo.pending_name != "") | (CharacterInfo.needs_rename == True))
query = db.session.query().select_from(CharacterInfo).join(Account).filter((CharacterInfo.pending_name != "") | (CharacterInfo.needs_rename is True))
else:
raise Exception("Not a valid filter")
@ -286,6 +291,4 @@ def get(status):
if perm_map & (1 << 6):
character["6"] += "Restricted Chat</br>"
return data

View File

@ -1,19 +1,20 @@
import click
import json
from flask.cli import with_appcontext
import random, string, datetime
import random
import string
import datetime
from flask_user import current_app
from app import db
from app.models import Account, PlayKey, CharacterInfo, Property, PropertyContent, UGC
import pathlib
import zlib
import os
from wand import image
from wand.exceptions import BlobError as BE
import app.pylddlib as ldd
from multiprocessing import Pool
from functools import partial
@click.command("init_db")
@click.argument('drop_tables', nargs=1)
@with_appcontext
@ -45,6 +46,7 @@ def init_accounts():
return
@click.command("load_property")
@click.argument('zone')
@click.argument('player')
@ -95,6 +97,7 @@ def load_property(zone, player):
)
new_prop_content.save()
@click.command("gen_image_cache")
def gen_image_cache():
"""generates image cache"""
@ -113,6 +116,7 @@ def gen_image_cache():
except BE:
return print(f"Error on {file}")
@click.command("gen_model_cache")
def gen_model_cache():
"""generate model obj cache"""
@ -123,6 +127,7 @@ def gen_model_cache():
pool.map(partial(convert_lxfml_to_obj, lod=1), files)
pool.map(partial(convert_lxfml_to_obj, lod=2), files)
def convert_lxfml_to_obj(file, lod):
mtl = get_cache_file(file).with_suffix(f".lod{lod}.mtl")
if not mtl.exists():
@ -136,6 +141,7 @@ def convert_lxfml_to_obj(file, lod):
# print(f"Already Exists: {file} with LOD {lod}")
return
def get_cache_file(path):
"""helper"""
# convert to list so that we can change elements
@ -147,6 +153,7 @@ def get_cache_file(path):
return pathlib.Path(*parts)
def find_or_create_account(name, email, password, gm_level=9):
""" Find existing account or create new account """
account = Account.query.filter(Account.email == email).first()
@ -164,7 +171,8 @@ def find_or_create_account(name, email, password, gm_level=9):
db.session.commit()
play_key = PlayKey.query.filter(PlayKey.key_string == key).first()
account = Account(email=email,
account = Account(
email=email,
username=name,
password=current_app.user_manager.password_manager.hash_password(password),
play_key_id=play_key.id,

View File

@ -16,13 +16,13 @@ from wtforms import (
SubmitField,
validators,
IntegerField,
StringField,
SelectField
)
from wtforms.validators import DataRequired, Optional
from app.models import PlayKey
def validate_play_key(form, field):
"""Validates a field for a valid phone number
Args:
@ -85,6 +85,7 @@ class CustomRegisterForm(FlaskForm):
submit = SubmitField('Register')
class CreatePlayKeyForm(FlaskForm):
count = IntegerField(
@ -97,6 +98,7 @@ class CreatePlayKeyForm(FlaskForm):
)
submit = SubmitField('Create!')
class EditPlayKeyForm(FlaskForm):
active = BooleanField(

View File

@ -1,5 +1,5 @@
from flask import render_template, Blueprint, request, url_for
from flask_user import login_required, current_user
from flask_user import login_required
from app.models import CommandLog, ActivityLog, db, Account, CharacterInfo, AuditLog
from datatables import ColumnDT, DataTables
import time
@ -7,6 +7,7 @@ from app import gm_level
log_blueprint = Blueprint('log', __name__)
@log_blueprint.route('/activities', methods=['GET'])
@login_required
@gm_level(8)

View File

@ -19,6 +19,7 @@ import xml.etree.ElementTree as ET
luclient_blueprint = Blueprint('luclient', __name__)
locale = {}
@luclient_blueprint.route('/get_dds_as_png/<filename>')
@login_required
def get_dds_as_png(filename):
@ -69,7 +70,8 @@ def get_icon_lot(id):
)[0]
# find the asset from rendercomponent given the component id
filename = query_cdclient('select icon_asset from RenderComponent where id = ?',
filename = query_cdclient(
'select icon_asset from RenderComponent where id = ?',
[render_component_id],
one=True
)[0]
@ -117,6 +119,7 @@ def get_icon_iconid(id):
return send_file(pathlib.Path(cache).resolve())
@luclient_blueprint.route('/unknown')
@login_required
def unknown():
@ -195,6 +198,7 @@ def translate_from_locale(trans_string):
else:
return trans_string
def register_luclient_jinja_helpers(app):
@app.template_filter('get_zone_name')
@ -203,7 +207,9 @@ def register_luclient_jinja_helpers(app):
@app.template_filter('get_skill_desc')
def get_skill_desc(skill_id):
return translate_from_locale(f'SkillBehavior_{skill_id}_descriptionUI').replace(
return translate_from_locale(
f'SkillBehavior_{skill_id}_descriptionUI'
).replace(
"%(DamageCombo)", "Damage Combo: "
).replace(
"%(AltCombo)", "<br/>Skeleton Combo: "
@ -240,7 +246,7 @@ def register_luclient_jinja_helpers(app):
one=True
)
if intermed:
name = intermed[7] if (intermed[7] != "None" and intermed[7] !="" and intermed[7] != None) else intermed[1]
name = intermed[7] if (intermed[7] != "None" and intermed[7] != "" and intermed[7] is None) else intermed[1]
return name
@app.template_filter('get_lot_rarity')
@ -252,7 +258,8 @@ def register_luclient_jinja_helpers(app):
one=True
)[0]
rarity = query_cdclient('select rarity from ItemComponent where id = ?',
rarity = query_cdclient(
'select rarity from ItemComponent where id = ?',
[render_component_id],
one=True
)
@ -302,7 +309,6 @@ def register_luclient_jinja_helpers(app):
return consolidate_stats(stats)
@app.template_filter('get_set_stats')
def get_set_stats(lot_id):
stats = query_cdclient(
@ -342,7 +348,6 @@ def consolidate_stats(stats):
if stat[3]:
consolidated_stats["skill"].append([stat[3], stat[4]])
stats = consolidated_stats
elif len(stats) == 1:
stats = {

View File

@ -1,7 +1,6 @@
from flask import render_template, Blueprint, redirect, url_for, request, abort, flash, request
from flask import render_template, Blueprint, redirect, url_for, flash, request
from flask_user import login_required, current_user
from app.models import db, Mail, CharacterInfo
from datatables import ColumnDT, DataTables
from app.models import Mail, CharacterInfo
from app.forms import SendMailForm
from app import gm_level, log_audit
from app.luclient import translate_from_locale, query_cdclient
@ -42,7 +41,9 @@ def send():
attachment_lot=form.attachment.data,
attachment_count=form.attachment_count.data
).save()
log_audit(f"Sent {form.subject.data}: {form.body.data} to ({character.id}){character.name} with {form.attachment_count.data} of item {form.attachment.data}")
log_audit(f"Sent {form.subject.data}: \
{form.body.data} to ({character.id}){character.name} \
with {form.attachment_count.data} of item {form.attachment.data}")
else:
Mail(
sender_id=0,
@ -56,12 +57,13 @@ def send():
attachment_lot=form.attachment.data,
attachment_count=form.attachment_count.data
).save()
log_audit(f"Sent {form.subject.data}: {form.body.data} to ({form.recipient.data}){CharacterInfo.query.filter(CharacterInfo.id == form.recipient.data).first().name} with {form.attachment_count.data} of item {form.attachment.data}")
log_audit(f"Sent {form.subject.data}: \
{form.body.data} to ({form.recipient.data}){CharacterInfo.query.filter(CharacterInfo.id == form.recipient.data).first().name} \
with {form.attachment_count.data} of item {form.attachment.data}")
flash("Sent Mail", "success")
return redirect(url_for('mail.send'))
recipients = CharacterInfo.query.all()
for character in recipients:
form.recipient.choices.append((character.id, character.name))
@ -74,7 +76,7 @@ def send():
for item in items:
name = translate_from_locale(f'Objects_{item[0]}_name')
if name == f'Objects_{item[0]}_name':
name = (item[2] if (item[2] != "None" and item[2] !="" and item[2] != None) else item[1])
name = (item[2] if (item[2] != "None" and item[2] != "" and item[2] not None) else item[1])
form.attachment.choices.append(
(
item[0],

View File

@ -1,18 +1,15 @@
from flask import render_template, Blueprint, redirect, request, send_from_directory, make_response, send_file, current_app
from flask_user import login_required, current_user
import json, glob, os
from wand import image
from flask import render_template, Blueprint, send_from_directory
from flask_user import current_user
from app.models import Account, AccountInvitation, CharacterInfo
from app.models import Account
from app.schemas import AccountSchema, CharacterInfoSchema
from app.luclient import query_cdclient
from app import gm_level
main_blueprint = Blueprint('main', __name__)
account_schema = AccountSchema()
char_info_schema = CharacterInfoSchema()
@main_blueprint.route('/', methods=['GET'])
def index():
"""Home/Index Page"""

View File

@ -1,6 +1,6 @@
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_user import UserMixin, current_user
from flask_user import UserMixin
from wtforms import ValidationError
import logging
@ -12,6 +12,7 @@ from time import sleep
import random
import string
# retrying query to work around python trash collector
# killing connections of other gunicorn workers
class RetryingQuery(BaseQuery):
@ -46,9 +47,11 @@ class RetryingQuery(BaseQuery):
raise
self.session.rollback()
db = SQLAlchemy(query_class=RetryingQuery)
migrate = Migrate()
class PlayKey(db.Model):
__tablename__ = 'play_keys'
id = db.Column(db.Integer, primary_key=True)
@ -121,6 +124,7 @@ class PlayKey(db.Model):
db.session.commit()
db.session.refresh(self)
class Account(db.Model, UserMixin):
__tablename__ = 'accounts'
id = db.Column(
@ -200,7 +204,7 @@ class Account(db.Model, UserMixin):
@staticmethod
def get_user_by_id(*, user_id=None):
return User.query.filter(user_id == User.id).first()
return Account.query.filter(user_id == Account.id).first()
def save(self):
db.session.add(self)
@ -211,6 +215,7 @@ class Account(db.Model, UserMixin):
db.session.delete(self)
db.session.commit()
class AccountInvitation(db.Model):
__tablename__ = 'account_invites'
id = db.Column(db.Integer, primary_key=True)
@ -245,14 +250,10 @@ class AccountInvitation(db.Model):
db.session.delete(self)
db.session.commit()
@staticmethod
def get_user_by_id(*, user_id=None):
return User.query.filter(user_id == User.id).first()
return Account.query.filter(user_id == Account.id).first()
def delete(self):
db.session.delete(self)
db.session.commit()
# This table is cursed, see prop_clone_id
class CharacterInfo(db.Model):
@ -327,6 +328,7 @@ class CharacterInfo(db.Model):
db.session.delete(self)
db.session.commit()
class CharacterXML(db.Model):
__tablename__ = 'charxml'
id = db.Column(
@ -348,6 +350,7 @@ class CharacterXML(db.Model):
db.session.delete(self)
db.session.commit()
class CommandLog(db.Model):
__tablename__ = 'command_log'
id = db.Column(db.Integer, primary_key=True)
@ -378,6 +381,7 @@ class CommandLog(db.Model):
db.session.delete(self)
db.session.commit()
class Friends(db.Model):
__tablename__ = 'friends'
player_id = db.Column(
@ -423,6 +427,7 @@ class Friends(db.Model):
db.session.delete(self)
db.session.commit()
class Leaderboard(db.Model):
__tablename__ = 'leaderboard'
id = db.Column(db.Integer, primary_key=True)
@ -472,6 +477,7 @@ class Leaderboard(db.Model):
db.session.delete(self)
db.session.commit()
class Mail(db.Model):
__tablename__ = 'mail'
id = db.Column(
@ -560,6 +566,7 @@ class Mail(db.Model):
db.session.delete(self)
db.session.commit()
class ObjectIDTracker(db.Model):
__tablename__ = 'object_id_tracker'
last_object_id = db.Column(
@ -569,6 +576,7 @@ class ObjectIDTracker(db.Model):
server_default='0'
)
class PetNames(db.Model):
__tablename__ = 'pet_names'
id = db.Column(mysql.BIGINT, primary_key=True)
@ -763,6 +771,7 @@ class UGC(db.Model):
db.session.delete(self)
db.session.commit()
class PropertyContent(db.Model):
__tablename__ = 'properties_contents'
id = db.Column(
@ -843,6 +852,7 @@ class PropertyContent(db.Model):
db.session.delete(self)
db.session.commit()
class ActivityLog(db.Model):
__tablename__ = 'activity_log'
id = db.Column(mysql.INTEGER, primary_key=True)
@ -883,6 +893,7 @@ class ActivityLog(db.Model):
db.session.delete(self)
db.session.commit()
class BugReport(db.Model):
__tablename__ = 'bug_reports'
id = db.Column(mysql.INTEGER, primary_key=True)
@ -944,6 +955,7 @@ class BugReport(db.Model):
db.session.delete(self)
db.session.commit()
class Server(db.Model):
__tablename__ = 'servers'
id = db.Column(
@ -1016,6 +1028,7 @@ class Reports(db.Model):
db.session.delete(self)
db.session.commit()
class AuditLog(db.Model):
__tablename__ = 'audit_logs'
id = db.Column(

View File

@ -2,7 +2,6 @@ from flask import render_template, Blueprint, redirect, url_for, request, flash,
from flask_user import login_required
from app.models import PetNames, db, CharacterXML, CharacterInfo
from datatables import ColumnDT, DataTables
from app.forms import CreatePlayKeyForm, EditPlayKeyForm
from app import gm_level, log_audit, scheduler
moderation_blueprint = Blueprint('moderation', __name__)
@ -64,7 +63,6 @@ def get_pets(status="all"):
else:
raise Exception("Not a valid filter")
params = request.args.to_dict()
rowTable = DataTables(params, query, columns)
@ -116,7 +114,7 @@ def get_pets(status="all"):
{CharacterInfo.query.filter(CharacterInfo.id==pet_data['3']).first().name}
</a>
"""
except Exception as e:
except Exception:
PetNames.query.filter(PetNames.id == id).first().delete()
pet_data["0"] = "<span class='text-danger'>Deleted Refresh to make go away</span>"
pet_data["3"] = "<span class='text-danger'>Character Deleted</span>"
@ -131,7 +129,7 @@ def pet_name_maintenance():
with scheduler.app.app_context():
# associate pet names to characters
current_app.logger.info("Started Pet Name Maintenance")
unassociated_pets = PetNames.query.filter(PetNames.owner_id == None).all()
unassociated_pets = PetNames.query.filter(PetNames.owner_id is None).all()
if unassociated_pets:
current_app.logger.info("Found un-associated pets")
for pet in unassociated_pets:

View File

@ -1,14 +1,14 @@
from flask import render_template, Blueprint, redirect, url_for, request, abort, flash
from flask_user import login_required, current_user
from app.models import Account, AccountInvitation, PlayKey, db
from flask import render_template, Blueprint, redirect, url_for, request, flash
from flask_user import login_required
from app.models import Account, PlayKey, db
from datatables import ColumnDT, DataTables
from app.forms import CreatePlayKeyForm, EditPlayKeyForm
from app import gm_level, log_audit
play_keys_blueprint = Blueprint('play_keys', __name__)
# Key creation page
# Key creation page
@play_keys_blueprint.route('/', methods=['GET'])
@login_required
@gm_level(9)
@ -45,7 +45,7 @@ def bulk_create():
@gm_level(9)
def delete(id):
key = PlayKey.query.filter(PlayKey.id == id).first()
associated_accounts = Account.query.filter(Account.play_key_id==id).all()
# associated_accounts = Account.query.filter(Account.play_key_id==id).all()
log_audit(f"Deleted Play Key {key.key_string}")
flash(f"Deleted Play Key {key.key_string}", "danger")
key.delete()
@ -125,7 +125,12 @@ def get():
Delete
</a>
<div class="modal fade bd-example-modal-lg" id="delete-{play_key["1"]}-modal" tabindex="-1" role="dialog" aria-labelledby="delete-{play_key["1"]}-modalLabel" aria-hidden="true">
<div class="modal
fade bd-example-modal-lg"
id="delete-{play_key["1"]}-modal"
tabindex="-1" role="dialog"
aria-labelledby="delete-{play_key["1"]}-modalLabel"
aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content bg-dark border-primary">
<div class="modal-header">

View File

@ -5,14 +5,11 @@ from flask import (
url_for,
request,
abort,
jsonify,
send_from_directory,
make_response,
flash,
current_app
)
from flask_user import login_required, current_user
import json
from datatables import ColumnDT, DataTables
import time
from app.models import Property, db, UGC, CharacterInfo, PropertyContent, Account
@ -21,8 +18,6 @@ from app import gm_level, log_audit
from app.luclient import query_cdclient
import zlib
import xmltodict
import os
import app.pylddlib as ldd
import pathlib
@ -30,6 +25,7 @@ property_blueprint = Blueprint('properties', __name__)
property_schema = PropertySchema()
@property_blueprint.route('/', methods=['GET'])
@login_required
@gm_level(3)
@ -89,8 +85,6 @@ def approve(id):
else:
go_to = url_for('main.index')
return redirect(go_to)
@ -137,13 +131,16 @@ def get(status="all"):
if status == "all":
query = db.session.query().select_from(Property).join(CharacterInfo, CharacterInfo.id == Property.owner_id).join(Account)
elif status == "approved":
query = db.session.query().select_from(Property).join(CharacterInfo, CharacterInfo.id==Property.owner_id).join(Account).filter(Property.mod_approved==True).filter(Property.privacy_option==2)
query = db.session.query().select_from(Property).join(
CharacterInfo, CharacterInfo.id == Property.owner_id
).join(Account).filter(Property.mod_approved is True).filter(Property.privacy_option == 2)
elif status == "unapproved":
query = db.session.query().select_from(Property).join(CharacterInfo, CharacterInfo.id==Property.owner_id).join(Account).filter(Property.mod_approved==False).filter(Property.privacy_option==2)
query = db.session.query().select_from(Property).join(
CharacterInfo, CharacterInfo.id == Property.owner_id
).join(Account).filter(Property.mod_approved is False).filter(Property.privacy_option == 2)
else:
raise Exception("Not a valid filter")
params = request.args.to_dict()
rowTable = DataTables(params, query, columns)
@ -242,6 +239,7 @@ def view_model(id, lod):
lod=lod
)
property_center = {
1150: "(-17, 432, -60)",
1151: "(0, 455, -110)",
@ -304,6 +302,7 @@ def view_models(id, lod):
lod=lod
)
@property_blueprint.route('/get_model/<id>/<file_format>/<lod>', methods=['GET'])
@login_required
def get_model(id, file_format, lod):
@ -312,14 +311,13 @@ def get_model(id, file_format, lod):
abort(404)
if content.lot == 14: # ugc model
response = ugc(content)[0]
else: # prebuild model
else: # prebuilt model
response = prebuilt(content, file_format, lod)[0]
response.headers.set('Content-Type', 'text/xml')
return response
@property_blueprint.route('/download_model/<id>', methods=['GET'])
@login_required
def download_model(id):
@ -327,7 +325,7 @@ def download_model(id):
if content.lot == 14: # ugc model
response, filename = ugc(content)
else: # prebuild model
else: # prebuilt model
response, filename = prebuilt(content, "lxfml")
response.headers.set('Content-Type', 'attachment/xml')
@ -355,7 +353,8 @@ def prebuilt(content, file_format, lod):
one=True
)[0]
# find the asset from rendercomponent given the component id
filename = query_cdclient('select render_asset from RenderComponent where id = ?',
filename = query_cdclient(
'select render_asset from RenderComponent where id = ?',
[render_component_id],
one=True
)

View File

@ -1,33 +1,12 @@
#!/usr/bin/env python
# pylddlib version 0.4.9.7
# based on pyldd2obj version 0.4.8 - Copyright (c) 2019 by jonnysp
#
# Updates:
# 0.4.9.8 Make work with LEGO Universe brickdb
# 0.4.9.7 corrected bug of incorrectly parsing the primitive xml file, specifically with comments. Add support LDDLIFTREE envirnment variable to set location of db.lif.
# 0.4.9.6 preliminary Linux support
# 0.4.9.5 corrected bug of incorrectly Bounding / GeometryBounding parsing the primitive xml file.
# 0.4.9.4 improved lif.db checking for crucial files (because of the infamous botched 4.3.12 LDD Windows update).
# 0.4.9.3 improved Windows and Python 3 compatibility
# 0.4.9.2 changed handling of material = 0 for a part. Now a 0 will choose the 1st material (the base material of a part) and not the previous material of the subpart before. This will fix "Chicken Helmet Part 11262". It may break other parts and this change needs further regression.
# 0.4.9.1 improved custom2DField handling, fixed decorations bug, improved material assignments handling
# 0.4.9 updates to support reading extracted db.lif from db folder
#
# License: MIT License
#
import os
import platform
import sys
import math
import struct
import zipfile
from xml.dom import minidom
import time
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf-8')
PRIMITIVEPATH = '/Primitives/'
GEOMETRIEPATH = PRIMITIVEPATH
@ -36,6 +15,7 @@ MATERIALNAMESPATH = '/MaterialNames/'
LOGOONSTUDSCONNTYPE = {"0:4", "0:4:1", "0:4:2", "0:4:33", "2:4:1", "2:4:34"}
class Matrix3D:
def __init__(self, n11=1, n12=0, n13=0, n14=0, n21=0, n22=1, n23=0, n24=0, n31=0, n32=0, n33=1, n34=0, n41=0, n42=0, n43=0, n44=1):
self.n11 = n11
@ -56,7 +36,24 @@ class Matrix3D:
self.n44 = n44
def __str__(self):
return '[{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15}]'.format(self.n11, self.n12, self.n13,self.n14,self.n21, self.n22, self.n23,self.n24,self.n31, self.n32, self.n33,self.n34,self.n41, self.n42, self.n43,self.n44)
return '[{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15}]'.format(
self.n11,
self.n12,
self.n13,
self.n14,
self.n21,
self.n22,
self.n23,
self.n24,
self.n31,
self.n32,
self.n33,
self.n34,
self.n41,
self.n42,
self.n43,
self.n44
)
def rotate(self, angle=0, axis=0):
c = math.cos(angle)
@ -111,6 +108,7 @@ class Matrix3D:
self.n14 * other.n41 + self.n24 * other.n42 + self.n34 * other.n43 + self.n44 * other.n44
)
class Point3D:
def __init__(self, x=0, y=0, z=0):
self.x = x
@ -142,40 +140,57 @@ class Point3D:
def copy(self):
return Point3D(x=self.x, y=self.y, z=self.z)
class Point2D:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return '[{0},{1}]'.format(self.x, self.y * -1)
def string(self, prefix="t"):
return '{0} {1:f} {2:f}\n'.format(prefix, self.x, self.y * -1)
def copy(self):
return Point2D(x=self.x, y=self.y)
class Face:
def __init__(self, a=0, b=0, c=0):
self.a = a
self.b = b
self.c = c
def string(self, prefix="f", indexOffset=0, textureoffset=0):
if textureoffset == 0:
return prefix + ' {0}//{0} {1}//{1} {2}//{2}\n'.format(self.a + indexOffset, self.b + indexOffset, self.c + indexOffset)
else:
return prefix + ' {0}/{3}/{0} {1}/{4}/{1} {2}/{5}/{2}\n'.format(self.a + indexOffset, self.b + indexOffset, self.c + indexOffset,self.a + textureoffset, self.b + textureoffset, self.c + textureoffset)
return prefix + ' {0}/{3}/{0} {1}/{4}/{1} {2}/{5}/{2}\n'.format(
self.a + indexOffset,
self.b + indexOffset,
self.c + indexOffset,
self.a + textureoffset,
self.b + textureoffset,
self.c + textureoffset
)
def __str__(self):
return '[{0},{1},{2}]'.format(self.a, self.b, self.c)
class Group:
def __init__(self, node):
self.partRefs = node.getAttribute('partRefs').split(',')
class Bone:
def __init__(self, node):
self.refID = node.getAttribute('refID')
(a, b, c, d, e, f, g, h, i, x, y, z) = map(float, node.getAttribute('transformation').split(','))
self.matrix = Matrix3D(n11=a, n12=b, n13=c, n14=0, n21=d, n22=e, n23=f, n24=0, n31=g, n32=h, n33=i, n34=0, n41=x, n42=y, n43=z, n44=1)
class Part:
def __init__(self, node):
self.isGrouped = False
@ -185,19 +200,17 @@ class Part:
self.designID = node.getAttribute('designID')
self.materials = list(map(str, node.getAttribute('materials').split(',')))
lastm = '0'
for i, m in enumerate(self.materials):
if (m == '0'):
# self.materials[i] = lastm
self.materials[i] = self.materials[0] # in case of 0 choose the 'base' material
else:
lastm = m
if node.hasAttribute('decoration'):
self.decoration = list(map(str, node.getAttribute('decoration').split(',')))
for childnode in node.childNodes:
if childnode.nodeName == 'Bone':
self.Bones.append(Bone(node=childnode))
class Brick:
def __init__(self, node):
self.refID = node.getAttribute('refID')
@ -207,6 +220,7 @@ class Brick:
if childnode.nodeName == 'Part':
self.Parts.append(Part(node=childnode))
class SceneCamera:
def __init__(self, node):
self.refID = node.getAttribute('refID')
@ -215,6 +229,7 @@ class SceneCamera:
self.fieldOfView = float(node.getAttribute('fieldOfView'))
self.distance = float(node.getAttribute('distance'))
class Scene:
def __init__(self, file):
self.Bricks = []
@ -262,6 +277,7 @@ class Scene:
# print('Scene "'+ self.Name + '" Brickversion: ' + str(self.Version))
class GeometryReader:
def __init__(self, data):
self.offset = 0
@ -330,6 +346,7 @@ class GeometryReader:
self.offset += 4
return ret
class Geometry:
def __init__(self, designID, database):
self.designID = designID
@ -348,10 +365,14 @@ class Geometry:
self.Partname = primitive.Designname
self.studsFields2D = primitive.Fields2D
try:
geoBoundingList = [abs(float(primitive.Bounding['minX']) - float(primitive.Bounding['maxX'])), abs(float(primitive.Bounding['minY']) - float(primitive.Bounding['maxY'])), abs(float(primitive.Bounding['minZ']) - float(primitive.Bounding['maxZ']))]
geoBoundingList = [
abs(float(primitive.Bounding['minX']) - float(primitive.Bounding['maxX'])),
abs(float(primitive.Bounding['minY']) - float(primitive.Bounding['maxY'])),
abs(float(primitive.Bounding['minZ']) - float(primitive.Bounding['maxZ']))
]
geoBoundingList.sort()
self.maxGeoBounding = geoBoundingList[-1]
except KeyError as e:
except KeyError:
# print('\nBounding errror in part {0}: {1}\n'.format(designID, e))
pass
@ -386,11 +407,15 @@ class Geometry:
count += self.Parts[part].texCount
return count
class Bone2:
def __init__(self, boneId=0, angle=0, ax=0, ay=0, az=0, tx=0, ty=0, tz=0):
self.boneId = boneId
rotationMatrix = Matrix3D()
rotationMatrix.rotate(angle = -angle * math.pi / 180.0,axis = Point3D(x=ax,y=ay,z=az))
rotationMatrix.rotate(
angle=(-angle * math.pi / 180.0),
axis=Point3D(x=ax, y=ay, z=az)
)
p = Point3D(x=tx, y=ty, z=tz)
p.transformW(rotationMatrix)
rotationMatrix.n41 -= p.x
@ -398,12 +423,16 @@ class Bone2:
rotationMatrix.n43 -= p.z
self.matrix = rotationMatrix
class Field2D:
def __init__(self, type=0, width=0, height=0, angle=0, ax=0, ay=0, az=0, tx=0, ty=0, tz=0, field2DRawData='none'):
self.type = type
self.field2DRawData = field2DRawData
rotationMatrix = Matrix3D()
rotationMatrix.rotate(angle = -angle * math.pi / 180.0, axis = Point3D(x=ax,y=ay,z=az))
rotationMatrix.rotate(
angle=(-angle * math.pi / 180.0),
axis=Point3D(x=ax, y=ay, z=az)
)
p = Point3D(x=tx, y=ty, z=tz)
p.transformW(rotationMatrix)
rotationMatrix.n41 -= p.x
@ -432,11 +461,16 @@ class Field2D:
def __str__(self):
return '[type="{0}" transform="{1}" custom2DField="{2}"]'.format(self.type, self.matrix, self.custom2DField)
class CollisionBox:
def __init__(self, sX=0, sY=0, sZ=0, angle=0, ax=0, ay=0, az=0, tx=0, ty=0, tz=0):
rotationMatrix = Matrix3D()
rotationMatrix.rotate(angle = -angle * math.pi / 180.0, axis = Point3D(x=ax,y=ay,z=az))
p = Point3D(x=tx,y=ty,z=tz)
rotationMatrix.rotate(
angle=(-angle * math.pi / 180.0),
axis=Point3D(x=ax, y=ay, z=az)
)
p = Point3D(x=tx, y=ty,
z=tz)
p.transformW(rotationMatrix)
rotationMatrix.n41 -= p.x
rotationMatrix.n42 -= p.y
@ -456,7 +490,10 @@ class CollisionBox:
self.positions.append(Point3D(x=sX, y=sY, z=sZ))
def __str__(self):
return '[0,0,0] [{0},0,0] [0,{1},0] [{0},{1},0] [0,0,{2}] [0,{1},{2}] [{0},0,{2}] [{0},{1},{2}]'.format(self.corner.x, self.corner.y, self.corner.z)
return '[0,0,0] [{0},0,0] [0,{1},0] [{0},{1},0] [0,0,{2}] [0,{1},{2}] [{0},0,{2}] [{0},{1},{2}]'.format(
self.corner.x, self.corner.y, self.corner.z
)
class Primitive:
def __init__(self, data):
@ -475,7 +512,18 @@ class Primitive:
if node.nodeName == 'Flex':
for node in node.childNodes:
if node.nodeName == 'Bone':
self.Bones.append(Bone2(boneId=int(node.getAttribute('boneId')), angle=float(node.getAttribute('angle')), ax=float(node.getAttribute('ax')), ay=float(node.getAttribute('ay')), az=float(node.getAttribute('az')), tx=float(node.getAttribute('tx')), ty=float(node.getAttribute('ty')), tz=float(node.getAttribute('tz'))))
self.Bones.append(
Bone2(
boneId=int(node.getAttribute('boneId')),
angle=float(node.getAttribute('angle')),
ax=float(node.getAttribute('ax')),
ay=float(node.getAttribute('ay')),
az=float(node.getAttribute('az')),
tx=float(node.getAttribute('tx')),
ty=float(node.getAttribute('ty')),
tz=float(node.getAttribute('tz'))
)
)
elif node.nodeName == 'Annotations':
for childnode in node.childNodes:
if childnode.nodeName == 'Annotation' and childnode.hasAttribute('designname'):
@ -483,23 +531,73 @@ class Primitive:
elif node.nodeName == 'Collision':
for childnode in node.childNodes:
if childnode.nodeName == 'Box':
self.CollisionBoxes.append(CollisionBox(sX=float(childnode.getAttribute('sX')), sY=float(childnode.getAttribute('sY')), sZ=float(childnode.getAttribute('sZ')), angle=float(childnode.getAttribute('angle')), ax=float(childnode.getAttribute('ax')), ay=float(childnode.getAttribute('ay')), az=float(childnode.getAttribute('az')), tx=float(childnode.getAttribute('tx')), ty=float(childnode.getAttribute('ty')), tz=float(childnode.getAttribute('tz'))))
self.CollisionBoxes.append(
CollisionBox(
sX=float(childnode.getAttribute('sX')),
sY=float(childnode.getAttribute('sY')),
sZ=float(childnode.getAttribute('sZ')),
angle=float(childnode.getAttribute('angle')),
ax=float(childnode.getAttribute('ax')),
ay=float(childnode.getAttribute('ay')),
az=float(childnode.getAttribute('az')),
tx=float(childnode.getAttribute('tx')),
ty=float(childnode.getAttribute('ty')),
tz=float(childnode.getAttribute('tz'))
)
)
elif node.nodeName == 'PhysicsAttributes':
self.PhysicsAttributes = {"inertiaTensor": node.getAttribute('inertiaTensor'),"centerOfMass": node.getAttribute('centerOfMass'),"mass": node.getAttribute('mass'),"frictionType": node.getAttribute('frictionType')}
self.PhysicsAttributes = {
"inertiaTensor": node.getAttribute('inertiaTensor'),
"centerOfMass": node.getAttribute('centerOfMass'),
"mass": node.getAttribute('mass'),
"frictionType": node.getAttribute('frictionType')
}
elif node.nodeName == 'Bounding':
for childnode in node.childNodes:
if childnode.nodeName == 'AABB':
self.Bounding = {"minX": childnode.getAttribute('minX'), "minY": childnode.getAttribute('minY'), "minZ": childnode.getAttribute('minZ'), "maxX": childnode.getAttribute('maxX'), "maxY": childnode.getAttribute('maxY'), "maxZ": childnode.getAttribute('maxZ')}
self.Bounding = {
"minX": childnode.getAttribute('minX'),
"minY": childnode.getAttribute('minY'),
"minZ": childnode.getAttribute('minZ'),
"maxX": childnode.getAttribute('maxX'),
"maxY": childnode.getAttribute('maxY'),
"maxZ": childnode.getAttribute('maxZ')
}
elif node.nodeName == 'GeometryBounding':
for childnode in node.childNodes:
if childnode.nodeName == 'AABB':
self.GeometryBounding = {"minX": childnode.getAttribute('minX'), "minY": childnode.getAttribute('minY'), "minZ": childnode.getAttribute('minZ'), "maxX": childnode.getAttribute('maxX'), "maxY": childnode.getAttribute('maxY'), "maxZ": childnode.getAttribute('maxZ')}
self.GeometryBounding = {
"minX": childnode.getAttribute('minX'),
"minY": childnode.getAttribute('minY'),
"minZ": childnode.getAttribute('minZ'),
"maxX": childnode.getAttribute('maxX'),
"maxY": childnode.getAttribute('maxY'),
"maxZ": childnode.getAttribute('maxZ')
}
elif node.nodeName == 'Connectivity':
for childnode in node.childNodes:
if childnode.nodeName == 'Custom2DField':
self.Fields2D.append(Field2D(type=int(childnode.getAttribute('type')), width=int(childnode.getAttribute('width')), height=int(childnode.getAttribute('height')), angle=float(childnode.getAttribute('angle')), ax=float(childnode.getAttribute('ax')), ay=float(childnode.getAttribute('ay')), az=float(childnode.getAttribute('az')), tx=float(childnode.getAttribute('tx')), ty=float(childnode.getAttribute('ty')), tz=float(childnode.getAttribute('tz')), field2DRawData=str(childnode.firstChild.data)))
self.Fields2D.append(
Field2D(
type=int(childnode.getAttribute('type')),
width=int(childnode.getAttribute('width')),
height=int(childnode.getAttribute('height')),
angle=float(childnode.getAttribute('angle')),
ax=float(childnode.getAttribute('ax')),
ay=float(childnode.getAttribute('ay')),
az=float(childnode.getAttribute('az')),
tx=float(childnode.getAttribute('tx')),
ty=float(childnode.getAttribute('ty')),
tz=float(childnode.getAttribute('tz')),
field2DRawData=str(childnode.firstChild.data)
)
)
elif node.nodeName == 'Decoration':
self.Decoration = {"faces": node.getAttribute('faces'), "subMaterialRedirectLookupTable": node.getAttribute('subMaterialRedirectLookupTable')}
self.Decoration = {
"faces": node.getAttribute('faces'),
"subMaterialRedirectLookupTable": node.getAttribute('subMaterialRedirectLookupTable')
}
class Materials:
def __init__(self, data):
@ -519,6 +617,7 @@ class Materials:
def getMaterialbyId(self, mid):
return self.Materials[mid]
class Material:
def __init__(self, id, r, g, b, a, mtype):
self.id = id
@ -528,18 +627,25 @@ class Material:
self.g = float(g)
self.b = float(b)
self.a = float(a)
def string(self):
out = 'Kd {0} {1} {2}\nKa 1.600000 1.600000 1.600000\nKs 0.400000 0.400000 0.400000\nNs 3.482202\nTf 1 1 1\n'.format( self.r / 255, self.g / 255,self.b / 255)
out = 'Kd {0} {1} {2}\nKa 1.600000 1.600000 1.600000\nKs 0.400000 0.400000 0.400000\nNs 3.482202\nTf 1 1 1\n'.format(
self.r / 255,
self.g / 255,
self.b / 255
)
if self.a < 255:
out += 'Ni 1.575\n' + 'd {0}'.format(0.05) + '\n' + 'Tr {0}\n'.format(0.05)
return out
class DBinfo:
def __init__(self, data):
xml = minidom.parseString(data)
self.Version = xml.getElementsByTagName('Bricks')[0].attributes['version'].value
# print('DB Version: ' + str(self.Version))
class DBFolderFile:
def __init__(self, name, handle):
self.handle = handle
@ -554,6 +660,7 @@ class DBFolderFile:
finally:
reader.close()
class LIFFile:
def __init__(self, name, offset, size, handle):
self.handle = handle
@ -565,6 +672,7 @@ class LIFFile:
self.handle.seek(self.offset, 0)
return self.handle.read(self.size)
class DBFolderReader:
def __init__(self, folder):
self.filelist = {}
@ -574,7 +682,7 @@ class DBFolderReader:
try:
os.path.isdir(self.location)
except Exception as e:
except Exception:
self.initok = False
# print("db folder read FAIL")
return
@ -593,7 +701,6 @@ class DBFolderReader:
# print(MATERIALNAMESPATH)
pass
def fileexist(self, filename):
return filename in self.filelist
@ -603,6 +710,7 @@ class DBFolderReader:
entryName = os.path.join(path, name)
self.filelist[entryName] = DBFolderFile(name=entryName, handle=entryName)
class LIFReader:
def __init__(self, file):
self.packedFilesOffset = 84
@ -614,7 +722,7 @@ class LIFReader:
try:
self.filehandle = open(self.location, "rb")
self.filehandle.seek(0, 0)
except Exception as e:
except Exception:
self.initok = False
# print("Database FAIL")
return
@ -648,7 +756,7 @@ class LIFReader:
entryType = self.readShort(offset=offset)
offset += 6
entryName = '{0}{1}'.format(prefix,'/');
entryName = '{0}{1}'.format(prefix, '/')
self.filehandle.seek(offset + 1, 0)
if sys.version_info < (3, 0):
t = ord(self.filehandle.read(1))
@ -692,17 +800,18 @@ class LIFReader:
else:
return int.from_bytes(self.filehandle.read(2), byteorder='big')
class Converter:
def LoadDBFolder(self, dbfolderlocation):
self.database = DBFolderReader(folder=dbfolderlocation)
if self.database.initok and self.database.fileexist(os.path.join(dbfolderlocation, 'Materials.xml')):
self.allMaterials = Materials(data=self.database.filelist[os.path.join(dbfolderlocation,'Materials.xml')].read());
self.allMaterials = Materials(data=self.database.filelist[os.path.join(dbfolderlocation, 'Materials.xml')].read())
def LoadDatabase(self, databaselocation):
self.database = LIFReader(file=databaselocation)
if self.database.initok and self.database.fileexist('/Materials.xml'):
self.allMaterials = Materials(data=self.database.filelist['/Materials.xml'].read());
self.allMaterials = Materials(data=self.database.filelist['/Materials.xml'].read())
def LoadScene(self, filename):
if self.database.initok:
@ -717,8 +826,6 @@ class Converter:
usedmaterials = []
geometriecache = {}
start_time = time.time()
out = open(filename + ".obj.tmp", "w+")
out.truncate(0)
out.write("mtllib " + filename + ".mtl" + '\n\n')
@ -772,13 +879,12 @@ class Converter:
last_color = 0
for part in geo.Parts:
# try catch here for possible problems in materials assignment of various g, g1, g2, .. files in lxf file
try:
materialCurrentPart = pa.materials[part]
last_color = pa.materials[part]
except IndexError:
# print('WARNING: {0}.g{1} has NO material assignment in lxf. Replaced with color {2}. Fix {0}.xml faces values.'.format(pa.designID, part, last_color))
materialCurrentPart = last_color
lddmat = self.allMaterials.getMaterialbyId(materialCurrentPart)
@ -801,7 +907,7 @@ class Converter:
f.write(self.database.filelist[decofilename].read())
f.close()
if not matname in usedmaterials:
if matname not in usedmaterials:
usedmaterials.append(matname)
outtext.write("newmtl " + matname + '\n')
outtext.write(lddmat.string())
@ -825,6 +931,7 @@ class Converter:
sys.stdout.write('%s\r' % (' '))
# print("--- %s seconds ---" % (time.time() - start_time))
def setDBFolderVars(dbfolderlocation, lod):
global PRIMITIVEPATH
global GEOMETRIEPATH
@ -836,42 +943,6 @@ def setDBFolderVars(dbfolderlocation, lod):
MATERIALNAMESPATH = os.path.join(dbfolderlocation, 'MaterialNames', '')
# print(MATERIALNAMESPATH)
def FindDatabase():
lddliftree = os.getenv('LDDLIFTREE')
if lddliftree is not None:
if os.path.isdir(str(lddliftree)): #LDDLIFTREE points to folder
return str(lddliftree)
elif os.path.isfile(str(lddliftree)): #LDDLIFTREE points to file (should be db.lif)
return str(lddliftree)
else: #Env variable LDDLIFTREE not set. Check for default locations per different platform.
if platform.system() == 'Darwin':
if os.path.isdir(str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'Library','Application Support','LEGO Company','LEGO Digital Designer','db'))):
return str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'Library','Application Support','LEGO Company','LEGO Digital Designer','db'))
elif os.path.isfile(str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'Library','Application Support','LEGO Company','LEGO Digital Designer','db.lif'))):
return str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'Library','Application Support','LEGO Company','LEGO Digital Designer','db.lif'))
else:
# print("no LDD database found please install LEGO-Digital-Designer")
os._exit()
elif platform.system() == 'Windows':
if os.path.isdir(str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'AppData','Roaming','LEGO Company','LEGO Digital Designer','db'))):
return str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'AppData','Roaming','LEGO Company','LEGO Digital Designer','db'))
elif os.path.isfile(str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'AppData','Roaming','LEGO Company','LEGO Digital Designer','db.lif'))):
return str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'AppData','Roaming','LEGO Company','LEGO Digital Designer','db.lif'))
else:
# print("no LDD database found please install LEGO-Digital-Designer")
os._exit()
elif platform.system() == 'Linux':
if os.path.isdir(str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'.wine','drive_c','users',os.getenv('USER'),'Application Data','LEGO Company','LEGO Digital Designer','db'))):
return str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'.wine','drive_c','users',os.getenv('USER'),'Application Data','LEGO Company','LEGO Digital Designer','db'))
elif os.path.isfile(str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'.wine','drive_c','users',os.getenv('USER'),'Application Data','LEGO Company','LEGO Digital Designer','db.lif'))):
return str(os.path.join(str(os.getenv('USERPROFILE') or os.getenv('HOME')),'.wine','drive_c','users',os.getenv('USER'),'Application Data','LEGO Company','LEGO Digital Designer','db.lif'))
else:
# print("no LDD database found please install LEGO-Digital-Designer")
os._exit()
else:
# print('Your OS {0} is not supported yet.'.format(platform.system()))
os._exit()
def progress(count, total, status='', suffix=''):
bar_len = 40
@ -882,6 +953,7 @@ def progress(count, total, status='', suffix = ''):
sys.stdout.write('Progress: [%s] %s%s %s %s\r' % (bar, percents, '%', suffix, status))
sys.stdout.flush()
def main(lxf_filename, obj_filename, lod="2"):
# print("- - - pylddlib - - -")
# print(" _ ")
@ -901,5 +973,6 @@ def main(lxf_filename, obj_filename, lod="2"):
converter.LoadScene(filename=lxf_filename)
converter.Export(filename=obj_filename)
if __name__ == "__main__":
main()

View File

@ -1,11 +1,13 @@
from flask import render_template, Blueprint, redirect, url_for, request, abort, flash, request, current_app
from flask_user import login_required, current_user
from app.models import db, CharacterInfo, Account, CharacterXML, Reports
from flask import render_template, Blueprint, current_app
from flask_user import login_required
from app.models import CharacterInfo, Account, CharacterXML, Reports
from app import gm_level, scheduler
import datetime, xmltodict, json
import datetime
import xmltodict
reports_blueprint = Blueprint('reports', __name__)
@reports_blueprint.route('/', methods=['GET', 'POST'])
@login_required
@gm_level(3)
@ -13,6 +15,7 @@ def index():
reports = Reports.query.distinct(Reports.date).group_by(Reports.date).all()
return render_template('reports/index.html.j2', reports=reports)
@reports_blueprint.route('/items/by_date/<date>', methods=['GET', 'POST'])
@login_required
@gm_level(3)
@ -20,6 +23,7 @@ def items_by_date(date):
data = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "items").first().data
return render_template('reports/items/by_date.html.j2', data=data, date=date)
@reports_blueprint.route('/currency/by_date/<date>', methods=['GET', 'POST'])
@login_required
@gm_level(3)
@ -27,6 +31,7 @@ def currency_by_date(date):
data = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "currency").first().data
return render_template('reports/currency/by_date.html.j2', data=data, date=date)
@reports_blueprint.route('/uscore/by_date/<date>', methods=['GET', 'POST'])
@login_required
@gm_level(3)
@ -45,7 +50,7 @@ def gen_item_report():
report = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "items").first()
# Only one report per day
if report != None:
if report not None:
current_app.logger.info(f"Item Report Already Generated for {date}")
return
@ -99,7 +104,7 @@ def gen_currency_report():
report = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "currency").first()
# Only one report per day
if report != None:
if report not None:
current_app.logger.info(f"Currency Report Already Generated for {date}")
return
@ -147,7 +152,7 @@ def gen_uscore_report():
report = Reports.query.filter(Reports.date == date).filter(Reports.report_type == "uscore").first()
# Only one report per day
if report != None:
if report not None:
current_app.logger.info(f"U-Score Report Already Generated for {date}")
return

View File

@ -1,7 +1,21 @@
from flask_marshmallow import Marshmallow
from app.models import *
from app.models import (
PlayKey
PetNames
Mail
UGC
PropertyContent
Property
CharacterXML
CharacterInfo
Account
AccountInvitation
ActivityLog
CommandLog
)
ma = Marshmallow()
class PlayKeySchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = PlayKey
@ -44,7 +58,6 @@ class PropertyContentSchema(ma.SQLAlchemyAutoSchema):
ugc = ma.Nested(UGCSchema)
class PropertySchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Property

6
pylama.ini Normal file
View File

@ -0,0 +1,6 @@
[pylama]
ignore = D203, D212, D213, D406, D407, D408, D409, D100, D104, D401, F722
max_line_length = 160
[pylama:mccabe]
max-complexity = 35