diff --git a/app/__init__.py b/app/__init__.py
new file mode 100644
index 0000000..8d1fcd2
--- /dev/null
+++ b/app/__init__.py
@@ -0,0 +1,16 @@
+from flask import Flask
+from flask.ext.sqlalchemy import SQLAlchemy
+
+from coreapi import CoreAPI
+
+#from flaskext.mysql import MySQL
+
+app = Flask(__name__)
+app.config.from_object('config')
+
+CoreAPI.check_app(app)
+coreapi = CoreAPI(app)
+
+db = SQLAlchemy(app)
+
+from app import controllers, models
diff --git a/app/controllers.py b/app/controllers.py
new file mode 100644
index 0000000..93a5f8f
--- /dev/null
+++ b/app/controllers.py
@@ -0,0 +1,161 @@
+from binascii import unhexlify
+from hashlib import sha256
+from datetime import datetime
+
+from braveapi.client import API
+from ecdsa.keys import SigningKey, VerifyingKey
+from ecdsa.curves import NIST256p
+
+from flask import render_template, flash, redirect, url_for, abort, request, jsonify, session, g
+from app import app, coreapi, db, models
+from .forms import ConfirmStanding, SearchStanding
+from .coreapi import CoreAPI, CoreSession
+
+import cgi
+
+
+CHECK_LOGIN_EXCLUDE_ENDPOINTS = ['authorize', 'authorized']
+@app.before_request
+def check_login():
+ if request.endpoint not in CHECK_LOGIN_EXCLUDE_ENDPOINTS:
+ if 'token' in session:
+ core_session = coreapi.get_session(session['token'])
+ # TODO: check perms here
+ g.core_session = core_session
+ g.user = core_session.get_user()
+ else:
+ return redirect('/authorize')
+
+@app.route('/session_info')
+def session_info():
+ if g.core_session:
+ return jsonify(g.core_session.info)
+ return "No active session"
+
+@app.route('/perm_info')
+def perm_info():
+ return "View: {0}
Edit: {1}
core.application.create: {2}" \
+ .format(g.core_session.has_view_perm(), g.core_session.has_edit_perm(), g.core_session.has_perm('core.application.create'))
+
+
+@app.route('/')
+@app.route('/standings')
+def index():
+ standings = models.Standing.query.all()
+
+ return render_template("standings.html",
+ title='Standings',
+ user=g.user,
+ standings=standings)
+
+@app.route('/add_standing', methods=['GET', 'POST'])
+def add_standing():
+
+ form = SearchStanding()
+ if form.validate_on_submit():
+
+ if form.entity_type.data == 'alliance':
+ flash('alliance')
+ else:
+ flash('corp')
+ preview_form = ConfirmStanding()
+ return render_template('standing_add_preview.html',
+ form = preview_form,
+ entity_type = form.entity_type.data,
+ standing = form.standing.data,
+ entity_id = 'id',
+ corporation_name = 'corp name',
+ corporation_ticker = 'corp ticker',
+ alliance_name = 'alliance name',
+ alliance_ticker = 'alliance ticker')
+ if form.errors:
+ for field_name, field_errors in form.errors.items():
+ flash(u"Error in %s: %s" % (form[field_name].label.text, "
".join(field_errors)))
+ return render_template('standing_add_search.html',
+ title='Add Standing',
+ form=form)
+
+
+@app.route('/do_edit_standing', methods=['GET', 'POST'])
+def do_edit_standing():
+ form = ConfirmStanding()
+ if form.validate_on_submit():
+ if form.entity_type == 'alliance':
+ flash('alliance')
+ models.Standing.query.filter_by()
+ else:
+ flash('corp')
+
+
+ if form.errors:
+ for field_name, field_errors in form.errors.items():
+ flash(u"Error in %s: %s" % (form[field_name].label.text, "
".join(field_errors)))
+ return redirect('/add_standing')
+
+# Perform the initial API call and direct the user.
+@app.route('/authorize')
+def authorize():
+ api = coreapi.get_api()
+
+ # Build Success/Failure Redirect URLs
+ success = str("http://"+app.config['SERVER_NAME']+url_for('authorized'))
+ failure = str("http://"+app.config['SERVER_NAME']+url_for('fail'))
+
+ # Make the authentication call to the CORE Service
+ result = api.core.authorize(success=success, failure=failure)
+
+ print result.__repr__()
+
+ if 'location' in result:
+ # Redirect based on the authentication request validity
+ return redirect(result.location)
+ else:
+ return 'Error authorizing app: {0}'.format(result.message)
+
+
+# Root URI
+@app.route('/authorized')
+def authorized():
+ # Perform the initial API call and direct the user.
+
+ api = coreapi.get_api()
+
+ # Build Success/Failure Redirect URLs
+ token = request.args.get('token', '')
+
+ if not token:
+ abort(401)
+
+ session['token'] = token
+
+ return redirect('/session_info')
+
+
+# Root URI
+@app.route('/fail')
+def fail():
+ abort(401)
+
+@app.route('/logout')
+@app.route('/ciao')
+def logout():
+ session.pop('token', None)
+ return "Logged out"
+
+@app.route('/posts')
+def posts():
+ user = {'nickname': 'Miguel'} # fake user
+ posts = [ # fake array of posts
+ {
+ 'author': {'nickname': 'John'},
+ 'body': 'Beautiful day in Portland!'
+ },
+ {
+ 'author': {'nickname': 'Susan'},
+ 'body': 'The Avengers movie was so cool!'
+ }
+ ]
+ return render_template("index.html",
+ title='Home',
+ user=user,
+ posts=posts)
diff --git a/app/coreapi.py b/app/coreapi.py
new file mode 100644
index 0000000..7c5e13a
--- /dev/null
+++ b/app/coreapi.py
@@ -0,0 +1,95 @@
+# CORE_ENDPOINT: Url of the CORE API server your connecting to. Must not have a trailing '/', usually points to /api on the deployed domain
+# CORE_APP_ID: The ID of your registered application on the application management page in Core
+# CORE_PRIVATE_KEY: Your applications Private ECDSA Key as generated in the check_app(app) function
+# CORE_CORE_PUBLIC_KEY: The Core API server's Public ECDSA Key, printed in HEX on the application management page.
+
+import textwrap
+
+from binascii import unhexlify
+from hashlib import sha256
+
+from ecdsa.keys import SigningKey, VerifyingKey
+from ecdsa.curves import NIST256p
+
+from braveapi.client import API
+
+class CoreAPI:
+ required_config_variables_ = ['CORE_ENDPOINT', 'CORE_APP_ID', 'CORE_PRIVATE_KEY', \
+ 'CORE_CORE_PUBLIC_KEY', 'CORE_VIEW_PERMISSION', 'CORE_EDIT_PERMISSION']
+
+ def __init__(self, app):
+ self.endpoint = app.config['CORE_ENDPOINT']
+ self.app_id = app.config['CORE_APP_ID']
+ self.private_key_string = app.config['CORE_PRIVATE_KEY']
+ self.core_public_key_string = app.config['CORE_CORE_PUBLIC_KEY']
+
+ self.private_key = SigningKey.from_string(unhexlify(self.private_key_string), curve=NIST256p, hashfunc=sha256)
+ self.core_public_key = VerifyingKey.from_string(unhexlify(self.core_public_key_string), curve=NIST256p, hashfunc=sha256)
+
+ self.view_perm = app.config['CORE_VIEW_PERMISSION']
+ self.edit_perm = app.config['CORE_EDIT_PERMISSION']
+
+ @staticmethod
+ def check_app(app):
+ try:
+ for var in CoreAPI.required_config_variables_:
+ app.config[var]
+ except KeyError as e:
+ private = SigningKey.generate(NIST256p, hashfunc=sha256)
+
+ error_message = "\n================================================================================\n"
+
+ error_message += "Core Service API identity, public, or private key missing.\n\n"
+
+ error_message += "Here's a new private key; update the api.private setting to reflect this.\n" + \
+ "%s \n\n" % private.to_string().encode('hex')
+
+ error_message += "Here's that key's public key; this is what you register with Core.\n" + \
+ "%s \n\n" % private.get_verifying_key().to_string().encode('hex')
+
+ error_message += textwrap.fill("After registering, make sure to populate all of the following in config.py: {0}" \
+ .format(", ".join(CoreAPI.required_config_variables_)))
+
+ error_message += "\n================================================================================\n\n"
+
+ print error_message
+ raise
+
+ def get_api(self):
+ return API(self.endpoint, self.app_id, self.private_key, self.core_public_key)
+
+ def get_session(self, token):
+ info = self.get_api().core.info(token=token)
+ return CoreSession(self, info)
+
+ def check_logged_in(self):
+ return
+
+
+class CoreSession:
+ def __init__(self, coreapi, info):
+ self.info = info
+ self.coreapi = coreapi
+
+ def has_perm(self, perm):
+ return perm in self.info.perms
+
+ def has_view_perm(self):
+ return self.has_perm(self.coreapi.view_perm)
+
+ def has_edit_perm(self):
+ return self.has_perm(self.coreapi.edit_perm)
+
+ def get_user(self):
+ alliance = None
+ if 'alliance' in self.info:
+ alliance = self.info.alliance.name
+ return User(self.info.character.name,
+ self.info.corporation.name,
+ alliance)
+
+class User:
+ def __init__(self, character, corporation, alliance):
+ self.character = character
+ self.corporation = corporation
+ self.alliance = alliance
diff --git a/app/forms.py b/app/forms.py
new file mode 100644
index 0000000..66e882a
--- /dev/null
+++ b/app/forms.py
@@ -0,0 +1,21 @@
+from flask.ext.wtf import Form
+from wtforms import StringField, BooleanField, RadioField, SubmitField, HiddenField
+from wtforms.validators import DataRequired
+
+class SearchStanding(Form):
+ entity_type = RadioField('Entity Type',
+ choices=[('alliance', 'Alliance'), ('corporation', 'Corporation')],
+ default='alliance',
+ validators=[DataRequired()])
+ search_text = StringField('Search', validators=[DataRequired()])
+ standing = RadioField('Standing',
+ choices=[('+10', '+10'), ('+5', '+5'), ('+2.5', '+2.5'), ('+1.1', '+1.1'), ('0', '0'), ('-5', '-5'), ('-10', '-10')],
+ default='+2.5',
+ validators=[DataRequired()])
+ add_by_id = SubmitField('Add By Id')
+ add_by_name = SubmitField('Add By Name')
+ add_by_ticker = SubmitField('Add By Ticker')
+
+class ConfirmStanding(Form):
+ entity_type = HiddenField('Entity Type', validators=[DataRequired()])
+ standing = HiddenField('Search', validators=[DataRequired()])
diff --git a/app/models.py b/app/models.py
new file mode 100644
index 0000000..6b5ff29
--- /dev/null
+++ b/app/models.py
@@ -0,0 +1,48 @@
+from app import db
+
+class Character(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ name = db.Column(db.String(1024), index=True, unique=True)
+ corporation_id = db.Column(db.Integer, db.ForeignKey('corporation.id'))
+ alliance_id = db.Column(db.Integer, db.ForeignKey('alliance.id'))
+
+class Corporation(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ name = db.Column(db.String(1024), index=True, unique=True)
+ ticker = db.Column(db.String(1024), index=True, unique=True)
+ alliance_id = db.Column(db.Integer, db.ForeignKey('alliance.id'))
+ characters = db.relationship('Character', backref='corporation', lazy='dynamic')
+ standings = db.relationship('Standing', backref='corporation', lazy='dynamic')
+
+class Alliance(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ name = db.Column(db.String(1024), index=True, unique=True)
+ ticker = db.Column(db.String(1024), index=True, unique=True)
+ characters = db.relationship('Character', backref='alliance', lazy='dynamic')
+ corporations = db.relationship('Corporation', backref='alliance', lazy='dynamic')
+ standings = db.relationship('Standing', backref='alliance', lazy='dynamic')
+
+class Standing(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ standing = db.Column(db.String(16), index=True)
+ corporation_id = db.Column(db.Integer, db.ForeignKey('corporation.id'))
+ alliance_id = db.Column(db.Integer, db.ForeignKey('alliance.id'))
+ creator_id = db.Column(db.Integer, db.ForeignKey('character.id'))
+ editor_id = db.Column(db.Integer, db.ForeignKey('character.id'))
+
+class StandingChange(db.Model):
+ id = db.Column(db.Integer, primary_key=True)
+ corporation_id = db.Column(db.Integer, db.ForeignKey('corporation.id'))
+ alliance_id = db.Column(db.Integer, db.ForeignKey('alliance.id'))
+ date = db.Column(db.DateTime)
+ character_name = db.Column(db.String(1024))
+ character_corporation_name = db.Column(db.String(1024))
+ character_corporation_ticker = db.Column(db.String(1024))
+ character_alliance_name = db.Column(db.String(1024))
+ character_alliance_ticker = db.Column(db.String(1024))
+
+ def find_or_create_corporation_standing(corporation_eve_id, character_eve_id):
+ standing = self.query.filter_by(corporation_id = corpororation_eve_id).first()
+ if not standing:
+ standing = self.__class__()
+
diff --git a/app/templates/base.html b/app/templates/base.html
new file mode 100644
index 0000000..8655a87
--- /dev/null
+++ b/app/templates/base.html
@@ -0,0 +1,47 @@
+
+
Alliance Ticker | +Alliance Name | +Corp Ticker | +Corp Name | +Standing | +
---|---|---|---|---|
{{ standing.alliance and standing.alliance.ticker }} | +{{ standing.alliance and standing.alliance.name }} | +{{ standing.corporation and standing.corporation.ticker }} | +{{ standing.corporation and standing.corporation.name }} | +{{ standing.standing }} | +