Commit f4fd1e91 authored by mouadh's avatar mouadh

Merge branch 'rest_services'

# Conflicts:
#	requirements.txt
parents fd3ff66e 99d84a3b
include *.rst *.txt *.ini *.py *.cfg *.yml
recursive-include olapy *
recursive-include cubes *
recursive-include cubes_templates *
recursive-include olapy/config *
prune tmp
prune instance
prune .git
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<olapy>
<database>
<!--<sgbd>postgres</sgbd>-->
<user_name>postgres</user_name>
<password>root</password>
<host>localhost</host>
</database>
</olapy>
from __future__ import absolute_import, division, print_function
from flask_script import Manager, prompt_bool
from olapy.web import app, db
from olapy.web.models import User
manager = Manager(app)
@manager.command
def initdb():
db.create_all()
db.session.add(
User(username="admin", email="admin@admin.com", password='admin'))
db.session.add(
User(username="demo", email="demo@demo.com", password="demo"))
db.session.commit()
print('Initialized the database')
@manager.command
def dropdb():
if prompt_bool('Are you sure you want to lose all your data? '):
db.drop_all()
print('Dropped the database')
if __name__ == '__main__':
manager.run()
This diff is collapsed.
from __future__ import absolute_import, division, print_function
from ..tools.connection import MyDB
import pandas.io.sql as psql
def _load_table_config_file(executer_instance, cube_obj):
"""
Load tables from config file.
:param cube_obj: cubes object
:return: tables dict with table name as key and DataFrame as value
"""
tables = {}
# just one facts table right now
executer_instance.facts = cube_obj.facts[0].table_name
db = MyDB(db=executer_instance.cube)
for table in cube_obj.dimensions:
value = psql.read_sql_query("SELECT * FROM {0}".format(table.name),
db.connection)
tables[table.name] = value[[
col for col in value.columns if col.lower()[-3:] != '_id'
]]
# update table display name
for dimension in cube_obj.dimensions:
if dimension.displayName and dimension.name and dimension.displayName != dimension.name:
tables[dimension.displayName] = tables[dimension.name][
dimension.columns]
executer_instance.dimension_display_name.append(dimension.name)
return tables
def _construct_star_schema_config_file(executer_instance, cubes_obj):
"""
Construct star schema DataFrame from configuration file.
:param cube_name: cube name (or database name)
:param cubes_obj: cubes object
:return: star schema DataFrame
"""
executer_instance.facts = cubes_obj.facts[0].table_name
db = MyDB(db=executer_instance.cube)
# load facts table
fusion = psql.read_sql_query(
"SELECT * FROM {0}".format(executer_instance.facts), db.connection)
for fact_key, dimension_and_key in cubes_obj.facts[0].keys.items():
df = psql.read_sql_query(
"SELECT * FROM {0}".format(dimension_and_key.split('.')[0]),
db.connection)
fusion = fusion.merge(
df, left_on=fact_key, right_on=dimension_and_key.split('.')[1])
# TODO CHOSE BETWEEN THOSES DF
# if separated dimensions
# fusion = fusion.merge(df, left_on=fact_key,right_on=dimension_and_key.split('.')[1])
# TODO CHOSE BETWEEN THOSES DF
# if facts contains all dimensions
# fusion = facts
# measures in config-file only
if cubes_obj.facts[0].measures:
executer_instance.measures = cubes_obj.facts[0].measures
return fusion
def _construct_web_star_schema_config_file(executer_instance, cubes_obj):
"""
Construct star schema DataFrame from configuration file.
:param cube_name: cube name (or database name)
:param cubes_obj: cubes object
:return: star schema DataFrame
"""
all_columns = []
executer_instance.facts = cubes_obj.facts[0].table_name
db = MyDB(db=executer_instance.cube)
# load facts table
if cubes_obj.facts[0].columns:
all_columns += cubes_obj.facts[0].columns
fusion = psql.read_sql_query(
"SELECT * FROM {0}".format(executer_instance.facts), db.connection)
tables = {}
for table in cubes_obj.tables:
tab = psql.read_sql_query("SELECT * FROM {0}".format(table.name),
db.connection)
try:
if table.columns:
tab = tab[table.columns]
except:
print("table columns doesn't exist")
print('pass with all columns')
try:
if table.new_names:
tab = tab.rename(columns=table.new_names)
except:
print("verify your old and new columns names")
print('pass with no change')
all_columns += list(tab.columns)
tables.update({table.name: tab})
# measures in config-file only
if cubes_obj.facts[0].measures:
executer_instance.measures = cubes_obj.facts[0].measures
all_columns += cubes_obj.facts[0].measures
for fact_key, dimension_and_key in cubes_obj.facts[0].keys.items():
dimension_name = dimension_and_key.split('.')[0]
if dimension_name in tables.keys():
df = tables[dimension_name]
else:
df = psql.read_sql_query(
"SELECT * FROM {0}".format(dimension_and_key.split('.')[0]),
db.connection)
# TODO check merge (how)
fusion = fusion.merge(
df, left_on=fact_key, right_on=dimension_and_key.split('.')[1], how='left',
# remove suffixe from dimension and keep the same column name for facts
suffixes=('', '_y'))
return fusion[[column for column in all_columns if 'id' != column[-2:]]]
from __future__ import absolute_import, division, print_function
import os
import pandas as pd
def _load_tables_csv_files(executer_instance):
"""
Load tables from csv files.
:return: tables dict with table name as key and dataframe as value
"""
tables = {}
cube = executer_instance.get_cube()
for file in os.listdir(cube):
# to remove file extension ".csv"
table_name = os.path.splitext(file)[0]
value = pd.read_csv(
os.path.join(cube, file), sep=executer_instance.sep)
tables[table_name] = value[[
col for col in value.columns if col.lower()[-3:] != '_id'
]]
return tables
def _construct_star_schema_csv_files(executer_instance):
"""
Construct star schema DataFrame from csv files.
:param cube_name: cube name (folder name)
:return: star schema DataFrame
"""
cube = executer_instance.get_cube()
# loading facts table
fusion = pd.read_csv(
os.path.join(cube, executer_instance.facts + '.csv'),
sep=executer_instance.sep)
for file_name in os.listdir(cube):
try:
fusion = fusion.merge(
pd.read_csv(
os.path.join(cube, file_name), sep=executer_instance.sep))
except:
print('No common column')
pass
return fusion
from __future__ import absolute_import, division, print_function
from ..tools.connection import MyDB
import pandas.io.sql as psql
def _load_tables_db(executer_instance):
"""
Load tables from database.
:return: tables dict with table name as key and dataframe as value
"""
tables = {}
db = MyDB(db=executer_instance.cube)
cursor = db.connection.cursor()
cursor.execute("""SELECT table_name FROM information_schema.tables
WHERE table_schema = 'public'""")
for table_name in cursor.fetchall():
value = psql.read_sql_query(
'SELECT * FROM "{0}" '.format(table_name[0]), db.connection)
tables[table_name[0]] = value[[
col for col in value.columns if col.lower()[-3:] != '_id'
]]
return tables
def _construct_star_schema_db(executer_instance):
"""
Construct star schema DataFrame from database.
:param cube_name: cube name (database name)
:return: star schema DataFrame
"""
db = MyDB(db=executer_instance.cube)
# load facts table
fusion = psql.read_sql_query(
'SELECT * FROM "{0}" '.format(executer_instance.facts), db.connection)
cursor = db.connection.cursor()
cursor.execute("""SELECT table_name FROM information_schema.tables
WHERE table_schema = 'public'""")
for db_table_name in cursor.fetchall():
try:
fusion = fusion.merge(
psql.read_sql_query("SELECT * FROM {0}".format(
db_table_name[0]), db.connection))
except:
print('No common column')
pass
return fusion
This diff is collapsed.
import psycopg2 as pg
# postgres connection
USERNAME = 'postgres'
PASSWORD = 'root'
HOST = 'localhost'
from olapy_config_file_parser import DbConfigParser
class MyDB(object):
"""Connect to sql database (postgres only right now)."""
def __init__(self,
username=USERNAME,
password=PASSWORD,
db=None,
host=HOST):
# @staticmethod
# def db_credentials():
# db_config = DbConfigParser()
# if db_config.config_file_exist():
# # many databases in the future maybe
# return db_config.get_db_credentials()[0]
# else:
# raise Exception('Missing database config file')
def __init__(self,db=None):
# TODO temporary
db_config = DbConfigParser()
db_credentials = db_config.get_db_credentials()[0]
username = db_credentials['user_name']
password = db_credentials['password']
host = db_credentials['host']
if db is None:
self.connection = pg.connect(
"user={0} password={1}".format(username, password))
# first i want to show all databases to user (in excel)
self.connection = pg.connect("user={0} password={1} host='{2}'".
format(username, password, host))
else:
# and then we connect to the user db
try:
self.connection = pg.connect(
"user={0} password={1} dbname='{2}' host='{3}'".format(
......
......@@ -37,6 +37,7 @@ class Dimension:
def __str__(self):
return str(self.__dict__)
class Cube:
"""Cube class used to encapsulate config file attributes."""
......@@ -51,3 +52,37 @@ class Cube:
def __str__(self):
return str(self.__dict__)
class Table:
"""Column class used to encapsulate config file attributes for web client."""
def __init__(self, **kwargs):
"""
:param kwargs: {
table_name : 'something',
old_column_name : 'something',
new_column_name : 'something'
}
"""
self.__dict__.update(kwargs)
def __str__(self):
return str(self.__dict__)
class Dashboard:
"""Column class used to encapsulate config file attributes for web client."""
def __init__(self, **kwargs):
"""
:param kwargs: {
table_name : 'something',
old_column_name : 'something',
new_column_name : 'something'
}
"""
self.__dict__.update(kwargs)
def __str__(self):
return str(self.__dict__)
\ No newline at end of file
from __future__ import absolute_import, division, print_function
import os
from lxml import etree
class DbConfigParser:
# TODO one config file (I will try to merge dimensions between them in web part)
def __init__(self,
config_path = None,
file_name='olapy-config.xml'):
"""
:param cube_path: path to cube (csv folders)
:param file_name: config file name (DEFAULT = cubes-config.xml)
"""
if config_path is None:
from os.path import expanduser
home_directory = expanduser("~")
self.cube_path = os.path.join(home_directory, 'olapy-data')
else:
self.cube_path = config_path
self.file_name = file_name
def config_file_exist(self):
"""
Check whether the config file exists or not.
:return: True | False
"""
return os.path.isfile(os.path.join(self.cube_path, self.file_name))
def get_db_credentials(self):
"""
Get all db credentials in the config file.
:return: list of cube name as key and cube source as value (csv or postgres) (right now only postgres is supported)
"""
with open(os.path.join(self.cube_path, self.file_name)) as config_file:
parser = etree.XMLParser()
tree = etree.parse(config_file, parser)
try:
return [
{
# 'sgbd': db.find('sgbd').text,
'user_name': db.find('user_name').text,
'password': db.find('password').text,
'host': db.find('host').text,
}
for db in tree.xpath('/olapy/database')
]
except:
raise ('missed name or source tags')
......@@ -13,7 +13,7 @@ from spyne.protocol.soap import Soap11
from spyne.server.wsgi import WsgiApplication
from ..mdx.tools.config_file_parser import ConfigParser
from ..services.models import DiscoverRequest # , AuthenticationError
from ..services.models import DiscoverRequest
from ..services.models import ExecuteRequest, Session
from .xmla_discover_tools import XmlaDiscoverTools
from .xmla_execute_tools import XmlaExecuteTools
......@@ -254,8 +254,7 @@ def start_server(write_on_file=False):
logging.getLogger('spyne.protocol.xml').setLevel(logging.DEBUG)
logging.info("listening to http://127.0.0.1:8000/xmla")
logging.info("wsdl is at: http://localhost:8000/xmla?wsdl")
server = make_server('127.0.0.1', 8000, wsgi_application)
# server = make_server('192.168.101.139', 8000, wsgi_application)
server = make_server('0.0.0.0', 8000, wsgi_application)
server.serve_forever()
......
import os
from logging import DEBUG
from flask import Flask
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
# app.config['SECRET_KEY'] = os.environ['SECRET_KEY']
app.config['SECRET_KEY'] = os.urandom(24)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir,
'olapy.db')
app.config['DEBUG'] = True
app.logger.setLevel(DEBUG)
db = SQLAlchemy(app)
login_manager = LoginManager()
login_manager.session_protection = 'strong'
login_manager.login_view = 'login'
login_manager.init_app(app)
# this import should at the bottom (app is used in views module)
import views
from __future__ import absolute_import, division, print_function
from flask_wtf import Form
from wtforms import BooleanField, PasswordField, StringField, SubmitField
from wtforms.fields import TextAreaField
from wtforms.validators import DataRequired
from ..core.mdx.parser.parse import MdxParser
class QueryForm(Form):
"""Query Form."""
mdx = TextAreaField(
"MDX Query",
validators=[DataRequired(message="Please enter the MDX Query")])
def validate(self):
"""Valide.
:return:
"""
parser = MdxParser()
if self.mdx.data:
try:
parser.parsing_mdx_query('all', query=self.mdx.data)
print(parser.parsing_mdx_query('all', query=self.mdx.data))
except:
self.mdx.errors = list(self.mdx.errors)
self.mdx.errors.append('Invalide MDX Query !!')
self.mdx.errors = tuple(self.mdx.errors)
return False
if not Form.validate(self):
return False
return True
class LoginForm(Form):
"""Loging Form."""
username = StringField(
'Your username:',
validators=[DataRequired(message="Please enter the Username")])
password = PasswordField(
'Password',
validators=[DataRequired(message="Please enter the Password")])
remember_me = BooleanField('Remember Me ')
submit = SubmitField('Log In')
from __future__ import absolute_import, division, print_function
import logging
import os
from logging.handlers import RotatingFileHandler
from os.path import expanduser
class Logs:
"""Class responsible of managing logs (users , mdx and xmla logs)."""
def __init__(self, file_name):
self.file_name = file_name + ".log"
self.root_path = self._create_log_file()
@staticmethod
def _create_log_file():
home_directory = expanduser("~")
location = os.path.join(home_directory, 'olapy-data', 'logs')
if not os.path.exists(location):
os.makedirs(location)
return location
def write_log(self, msg):
# Creation of the logger object that will serve us to write in the logs
logger = logging.getLogger()
# We set the logger level to DEBUG, so it writes everything
logger.setLevel(logging.DEBUG)
# Creation of a formatter that will add time, level
# Of each message when writing a message in the log
formatter = logging.Formatter(
'%(asctime)s :: %(levelname)s :: %(message)s')
# Creation of a handler which will redirect message of the log to
# file in append mode, with 1 backup and max size of 1MB
file_handler = RotatingFileHandler(
os.path.join(self.root_path, self.file_name), 'a', 1000000, 1)
# We put the level on DEBUG, we tell him that he must use the formatter
# Created earlier and add this handler to the logger
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
# Creation of a second handler that will redirect each log
# On the console
# Steam_handler = logging.StreamHandler ()
# Steam_handler.setLevel (logging.DEBUG)
# Logger.addHandler (steam_handler)
# It's time to spam your code with logs everywhere:
logger.info(msg)
# logger.warning('Testing %s', 'foo')
from __future__ import absolute_import, division, print_function
from flask_login import UserMixin
from werkzeug.security import check_password_hash, generate_password_hash
from ..web import db
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
password_hash = db.Column(db.String)
@property
def password(self):
raise AttributeError('password: write-only field')
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
@staticmethod
def get_by_username(username):
return User.query.filter_by(username=username).first()
def __repr__(self):
return '<User {}>'.format(self.username)
from __future__ import absolute_import, division, print_function
import six
class IFrame(object):
"""Frame in which we can drag and drop our columns."""
iframe = """
<iframe
width="{width}"
height="{height}"
src="{src}{params}"
frameborder="0"
allowfullscreen
></iframe>
"""
def __init__(self, src, width, height, **kwargs):
"""
Iframe
:param src:
:param width:
:param height:
:param kwargs:
"""
self.src = src
self.width = width
self.height = height
self.params = kwargs
def _repr_html_(self):
"""return the embed iframe."""
if self.params:
# try:
# from urllib.parse import urlencode # Py 3
# except ImportError:
params = "?" + six.moves.urllib.parse.urlencode(self.params)
else:
params = ""
return self.iframe.format(
src=self.src, width=self.width, height=self.height, params=params)
template = """
<!DOCTYPE html>
<html>
<head>
<title>PivotTable.js</title>
<!-- external libs from cdnjs -->
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.10/c3.min.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.71/jquery.csv-0.71.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.10/c3.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/pivottable/1.6.3/pivot.min.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pivottable/1.6.3/pivot.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pivottable/1.6.3/d3_renderers.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pivottable/1.6.3/c3_renderers.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/pivottable/1.6.3/export_renderers.min.js"></script>
<style>
body {font-family: Verdana;}
.node {
border: solid 1px white;
font: 10px sans-serif;
line-height: 12px;
overflow: hidden;
position: absolute;
text-indent: 2px;
}
.c3-line, .c3-focused {stroke-width: 3px !important;}
.c3-bar {stroke: white !important; stroke-width: 1;}
.c3 text { font-size: 12px; color: grey;}
.tick line {stroke: white;}
.c3-axis path {stroke: grey;}
.c3-circle { opacity: 1 !important; }
</style>
</head>
<body>
<script type="text/javascript">
$(function(){
if(window.location != window.parent.location)
$("<a>", {target:"_blank", href:""})
.text("[pop out]").prependTo($("body"));
$("#output").pivotUI(
$.csv.toArrays($("#output").text()),
{
renderers: $.extend(
$.pivotUtilities.renderers,
$.pivotUtilities.c3_renderers,
$.pivotUtilities.d3_renderers,
$.pivotUtilities.export_renderers
),
hiddenAttributes: [""]
}
).show();
});
</script>
<div id="output" style="display: none;">%s</div>
</body>
</html>
"""
def pivot_ui(df, outfile_path="pivottablejs.html", width="100%", height="500"):
"""
Create pivot table html page relative to DataFrame.
:param df: the DataFrame
:param outfile_path: html page name (can be the path also)
:param width: page width
:param height: page height
:return: IFrame (html page) that can be injected to other html page
"""
with open(outfile_path, 'w') as outfile:
outfile.write(template % df.to_csv())
return IFrame(src=outfile_path, width=width, height=height)
This diff is collapsed.
$(function() {
Morris.Area({
element: 'morris-area-chart',
data: [{
period: '2010 Q1',
iphone: 2666,
ipad: null,
itouch: 2647
}, {
period: '2010 Q2',
iphone: 2778,
ipad: 2294,
itouch: 2441
}, {
period: '2010 Q3',
iphone: 4912,
ipad: 1969,
itouch: 2501
}, {
period: '2010 Q4',
iphone: 3767,
ipad: 3597,
itouch: 5689
}, {
period: '2011 Q1',
iphone: 6810,
ipad: 1914,
itouch: 2293
}, {
period: '2011 Q2',
iphone: 5670,
ipad: 4293,
itouch: 1881
}, {
period: '2011 Q3',
iphone: 4820,
ipad: 3795,
itouch: 1588
}, {
period: '2011 Q4',
iphone: 15073,
ipad: 5967,
itouch: 5175
}, {
period: '2012 Q1',
iphone: 10687,
ipad: 4460,
itouch: 2028
}, {
period: '2012 Q2',
iphone: 8432,
ipad: 5713,
itouch: 1791
}],
xkey: 'period',
ykeys: ['iphone', 'ipad', 'itouch'],
labels: ['iPhone', 'iPad', 'iPod Touch'],
pointSize: 2,
hideHover: 'auto',
resize: true
});
Morris.Donut({
element: 'morris-donut-chart',
data: [{
label: "Download Sales",
value: 12
}, {
label: "In-Store Sales",
value: 30
}, {
label: "Mail-Order Sales",
value: 20
}],
resize: true
});
Morris.Bar({
element: 'morris-bar-chart',
data: [{
y: '2006',
a: 100,
b: 90
}, {
y: '2007',
a: 75,
b: 65
}, {
y: '2008',
a: 50,
b: 40
}, {
y: '2009',
a: 75,
b: 65
}, {
y: '2010',
a: 50,
b: 40
}, {
y: '2011',
a: 75,
b: 65
}, {
y: '2012',
a: 100,
b: 90
}],
xkey: 'y',
ykeys: ['a', 'b'],
labels: ['Series A', 'Series B'],
hideHover: 'auto',
resize: true
});
});
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
/*
Copyright 2014 Roland Bouman
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var defaultXmlaUrl = "http://127.0.0.1:8000/xmla";
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
$(function() {
$('#side-menu').metisMenu();
});
//Loads the correct sidebar on window load,
//collapses the sidebar on window resize.
// Sets the min-height of #page-wrapper to window size
$(function() {
$(window).bind("load resize", function() {
var topOffset = 50;
var width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width;
if (width < 768) {
$('div.navbar-collapse').addClass('collapse');
topOffset = 100; // 2-row-menu
} else {
$('div.navbar-collapse').removeClass('collapse');
}
var height = ((this.window.innerHeight > 0) ? this.window.innerHeight : this.screen.height) - 1;
height = height - topOffset;
if (height < 1) height = 1;
if (height > topOffset) {
$("#page-wrapper").css("min-height", (height) + "px");
}
});
var url = window.location;
// var element = $('ul.nav a').filter(function() {
// return this.href == url;
// }).addClass('active').parent().parent().addClass('in').parent();
var element = $('ul.nav a').filter(function() {
return this.href == url;
}).addClass('active').parent();
while (true) {
if (element.is('li')) {
element = element.parent().addClass('in').parent();
} else {
break;
}
}
});
This diff is collapsed.
This diff is collapsed.
@import "variables.less";
@import "mixins.less";
// Global Styles
body {
background-color: @gray-lightest;
}
// Wrappers
#wrapper {
width: 100%;
}
#page-wrapper {
padding: 0 15px;
min-height: 568px;
background-color: white;
}
@media(min-width:768px) {
#page-wrapper {
position: inherit;
margin: 0 0 0 250px;
padding: 0 30px;
border-left: 1px solid darken(@gray-lightest, 6.5%);
}
}
// Navigation
// --Topbar
.navbar-top-links {
margin-right: 0;
}
.navbar-top-links li {
display: inline-block;
}
.navbar-top-links li:last-child {
margin-right: 15px;
}
.navbar-top-links li a {
padding: 15px;
min-height: 50px;
}
.navbar-top-links .dropdown-menu li {
display: block;
}
.navbar-top-links .dropdown-menu li:last-child {
margin-right: 0;
}
.navbar-top-links .dropdown-menu li a {
padding: 3px 20px;
min-height: 0;
}
.navbar-top-links .dropdown-menu li a div {
white-space: normal;
}
.navbar-top-links .dropdown-messages,
.navbar-top-links .dropdown-tasks,
.navbar-top-links .dropdown-alerts {
width: 310px;
min-width: 0;
}
.navbar-top-links .dropdown-messages {
margin-left: 5px;
}
.navbar-top-links .dropdown-tasks {
margin-left: -59px;
}
.navbar-top-links .dropdown-alerts {
margin-left: -123px;
}
.navbar-top-links .dropdown-user {
right: 0;
left: auto;
}
// --Sidebar
.sidebar {
.sidebar-nav.navbar-collapse {
padding-left: 0;
padding-right: 0;
}
}
.sidebar .sidebar-search {
padding: 15px;
}
.sidebar ul li {
border-bottom: 1px solid darken(@gray-lightest, 6.5%);
a {
&.active {
background-color: @gray-lighter;
}
}
}
.sidebar .arrow {
float: right;
}
.sidebar .fa.arrow:before {
content: "\f104";
}
.sidebar .active > a > .fa.arrow:before {
content: "\f107";
}
.sidebar .nav-second-level li,
.sidebar .nav-third-level li {
border-bottom: none !important;
}
.sidebar .nav-second-level li a {
padding-left: 37px;
}
.sidebar .nav-third-level li a {
padding-left: 52px;
}
@media(min-width:768px) {
.sidebar {
z-index: 1;
position: absolute;
width: 250px;
margin-top: 51px;
}
.navbar-top-links .dropdown-messages,
.navbar-top-links .dropdown-tasks,
.navbar-top-links .dropdown-alerts {
margin-left: auto;
}
}
// Buttons
.btn-outline {
color: inherit;
background-color: transparent;
transition: all .5s;
}
.btn-primary.btn-outline {
color: @brand-primary;
}
.btn-success.btn-outline {
color: @brand-success;
}
.btn-info.btn-outline {
color: @brand-info;
}
.btn-warning.btn-outline {
color: @brand-warning;
}
.btn-danger.btn-outline {
color: @brand-danger;
}
.btn-primary.btn-outline:hover,
.btn-success.btn-outline:hover,
.btn-info.btn-outline:hover,
.btn-warning.btn-outline:hover,
.btn-danger.btn-outline:hover {
color: white;
}
// Chat Widget
.chat {
margin: 0;
padding: 0;
list-style: none;
}
.chat li {
margin-bottom: 10px;
padding-bottom: 5px;
border-bottom: 1px dotted @gray-light;
}
.chat li.left .chat-body {
margin-left: 60px;
}
.chat li.right .chat-body {
margin-right: 60px;
}
.chat li .chat-body p {
margin: 0;
}
.panel .slidedown .glyphicon,
.chat .glyphicon {
margin-right: 5px;
}
.chat-panel .panel-body {
height: 350px;
overflow-y: scroll;
}
// Login Page
.login-panel {
margin-top: 25%;
}
// Flot Charts Containers
.flot-chart {
display: block;
height: 400px;
}
.flot-chart-content {
width: 100%;
height: 100%;
}
// DataTables Overrides
table.dataTable thead .sorting,
table.dataTable thead .sorting_asc,
table.dataTable thead .sorting_desc,
table.dataTable thead .sorting_asc_disabled,
table.dataTable thead .sorting_desc_disabled {
background: transparent;
}
table.dataTable thead .sorting_asc:after {
content: "\f0de";
float: right;
font-family: fontawesome;
}
table.dataTable thead .sorting_desc:after {
content: "\f0dd";
float: right;
font-family: fontawesome;
}
table.dataTable thead .sorting:after {
content: "\f0dc";
float: right;
font-family: fontawesome;
color: rgba(50,50,50,.5);
}
// Circle Buttons
.btn-circle {
width: 30px;
height: 30px;
padding: 6px 0;
border-radius: 15px;
text-align: center;
font-size: 12px;
line-height: 1.428571429;
}
.btn-circle.btn-lg {
width: 50px;
height: 50px;
padding: 10px 16px;
border-radius: 25px;
font-size: 18px;
line-height: 1.33;
}
.btn-circle.btn-xl {
width: 70px;
height: 70px;
padding: 10px 16px;
border-radius: 35px;
font-size: 24px;
line-height: 1.33;
}
// Grid Demo Elements
.show-grid [class^="col-"] {
padding-top: 10px;
padding-bottom: 10px;
border: 1px solid #ddd;
background-color: #eee !important;
}
.show-grid {
margin: 15px 0;
}
// Custom Colored Panels
.huge {
font-size: 40px;
}
.panel-green {
border-color: @brand-success;
> .panel-heading {
border-color: @brand-success;
color: white;
background-color: @brand-success;
}
> a {
color: @brand-success;
&:hover {
color: darken(@brand-success, 15%);
}
}
}
.panel-red {
border-color: @brand-danger;
> .panel-heading {
border-color: @brand-danger;
color: white;
background-color: @brand-danger;
}
> a {
color: @brand-danger;
&:hover {
color: darken(@brand-danger, 15%);
}
}
}
.panel-yellow {
border-color: @brand-warning;
> .panel-heading {
border-color: @brand-warning;
color: white;
background-color: @brand-warning;
}
> a {
color: @brand-warning;
&:hover {
color: darken(@brand-warning, 15%);
}
}
}
// Timeline
.timeline {
position: relative;
padding: 20px 0 20px;
list-style: none;
}
.timeline:before {
content: " ";
position: absolute;
top: 0;
bottom: 0;
left: 50%;
width: 3px;
margin-left: -1.5px;
background-color: #eeeeee;
}
.timeline > li {
position: relative;
margin-bottom: 20px;
}
.timeline > li:before,
.timeline > li:after {
content: " ";
display: table;
}
.timeline > li:after {
clear: both;
}
.timeline > li:before,
.timeline > li:after {
content: " ";
display: table;
}
.timeline > li:after {
clear: both;
}
.timeline > li > .timeline-panel {
float: left;
position: relative;
width: 46%;
padding: 20px;
border: 1px solid #d4d4d4;
border-radius: 2px;
-webkit-box-shadow: 0 1px 6px rgba(0,0,0,0.175);
box-shadow: 0 1px 6px rgba(0,0,0,0.175);
}
.timeline > li > .timeline-panel:before {
content: " ";
display: inline-block;
position: absolute;
top: 26px;
right: -15px;
border-top: 15px solid transparent;
border-right: 0 solid #ccc;
border-bottom: 15px solid transparent;
border-left: 15px solid #ccc;
}
.timeline > li > .timeline-panel:after {
content: " ";
display: inline-block;
position: absolute;
top: 27px;
right: -14px;
border-top: 14px solid transparent;
border-right: 0 solid #fff;
border-bottom: 14px solid transparent;
border-left: 14px solid #fff;
}
.timeline > li > .timeline-badge {
z-index: 100;
position: absolute;
top: 16px;
left: 50%;
width: 50px;
height: 50px;
margin-left: -25px;
border-radius: 50% 50% 50% 50%;
text-align: center;
font-size: 1.4em;
line-height: 50px;
color: #fff;
background-color: #999999;
}
.timeline > li.timeline-inverted > .timeline-panel {
float: right;
}
.timeline > li.timeline-inverted > .timeline-panel:before {
right: auto;
left: -15px;
border-right-width: 15px;
border-left-width: 0;
}
.timeline > li.timeline-inverted > .timeline-panel:after {
right: auto;
left: -14px;
border-right-width: 14px;
border-left-width: 0;
}
.timeline-badge.primary {
background-color: #2e6da4 !important;
}
.timeline-badge.success {
background-color: #3f903f !important;
}
.timeline-badge.warning {
background-color: #f0ad4e !important;
}
.timeline-badge.danger {
background-color: #d9534f !important;
}
.timeline-badge.info {
background-color: #5bc0de !important;
}
.timeline-title {
margin-top: 0;
color: inherit;
}
.timeline-body > p,
.timeline-body > ul {
margin-bottom: 0;
}
.timeline-body > p + p {
margin-top: 5px;
}
@media(max-width:767px) {
ul.timeline:before {
left: 40px;
}
ul.timeline > li > .timeline-panel {
width: calc(100% - 90px);
width: -moz-calc(100% - 90px);
width: -webkit-calc(100% - 90px);
}
ul.timeline > li > .timeline-badge {
top: 16px;
left: 15px;
margin-left: 0;
}
ul.timeline > li > .timeline-panel {
float: right;
}
ul.timeline > li > .timeline-panel:before {
right: auto;
left: -15px;
border-right-width: 15px;
border-left-width: 0;
}
ul.timeline > li > .timeline-panel:after {
right: auto;
left: -14px;
border-right-width: 14px;
border-left-width: 0;
}
}
// Variables
@gray-darker: lighten(#000, 13.5%);
@gray-dark: lighten(#000, 20%);
@gray: lighten(#000, 33.5%);
@gray-light: lighten(#000, 60%);
@gray-lighter: lighten(#000, 93.5%);
@gray-lightest: lighten(#000, 97.25%);
@brand-primary: #428bca;
@brand-success: #5cb85c;
@brand-info: #5bc0de;
@brand-warning: #f0ad4e;
@brand-danger: #d9534f;
/*
* Social Buttons for Bootstrap
*
* Copyright 2013-2014 Panayiotis Lipiridis
* Licensed under the MIT License
*
* https://github.com/lipis/bootstrap-social
*/
@bs-height-base: (@line-height-computed + @padding-base-vertical * 2);
@bs-height-lg: (floor(@font-size-large * @line-height-base) + @padding-large-vertical * 2);
@bs-height-sm: (floor(@font-size-small * 1.5) + @padding-small-vertical * 2);
@bs-height-xs: (floor(@font-size-small * 1.2) + @padding-small-vertical + 1);
.btn-social {
position: relative;
padding-left: (@bs-height-base + @padding-base-horizontal);
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
> :first-child {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: @bs-height-base;
line-height: (@bs-height-base + 2);
font-size: 1.6em;
text-align: center;
border-right: 1px solid rgba(0, 0, 0, 0.2);
}
&.btn-lg {
padding-left: (@bs-height-lg + @padding-large-horizontal);
:first-child {
line-height: @bs-height-lg;
width: @bs-height-lg;
font-size: 1.8em;
}
}
&.btn-sm {
padding-left: (@bs-height-sm + @padding-small-horizontal);
:first-child {
line-height: @bs-height-sm;
width: @bs-height-sm;
font-size: 1.4em;
}
}
&.btn-xs {
padding-left: (@bs-height-xs + @padding-small-horizontal);
:first-child {
line-height: @bs-height-xs;
width: @bs-height-xs;
font-size: 1.2em;
}
}
}
.btn-social-icon {
.btn-social;
height: (@bs-height-base + 2);
width: (@bs-height-base + 2);
padding: 0;
:first-child {
border: none;
text-align: center;
width: 100%!important;
}
&.btn-lg {
height: @bs-height-lg;
width: @bs-height-lg;
padding-left: 0;
padding-right: 0;
}
&.btn-sm {
height: (@bs-height-sm + 2);
width: (@bs-height-sm + 2);
padding-left: 0;
padding-right: 0;
}
&.btn-xs {
height: (@bs-height-xs + 2);
width: (@bs-height-xs + 2);
padding-left: 0;
padding-right: 0;
}
}
.btn-social(@color-bg, @color: #fff) {
background-color: @color-bg;
.button-variant(@color, @color-bg, rgba(0,0,0,.2));
}
.btn-adn { .btn-social(#d87a68); }
.btn-bitbucket { .btn-social(#205081); }
.btn-dropbox { .btn-social(#1087dd); }
.btn-facebook { .btn-social(#3b5998); }
.btn-flickr { .btn-social(#ff0084); }
.btn-foursquare { .btn-social(#f94877); }
.btn-github { .btn-social(#444444); }
.btn-google-plus { .btn-social(#dd4b39); }
.btn-instagram { .btn-social(#3f729b); }
.btn-linkedin { .btn-social(#007bb6); }
.btn-microsoft { .btn-social(#2672ec); }
.btn-openid { .btn-social(#f7931e); }
.btn-pinterest { .btn-social(#cb2027); }
.btn-reddit { .btn-social(#eff7ff, #000); }
.btn-soundcloud { .btn-social(#ff5500); }
.btn-tumblr { .btn-social(#2c4762); }
.btn-twitter { .btn-social(#55acee); }
.btn-vimeo { .btn-social(#1ab7ea); }
.btn-vk { .btn-social(#587ea3); }
.btn-yahoo { .btn-social(#720e9e); }
/*
* Social Buttons for Bootstrap
*
* Copyright 2013-2014 Panayiotis Lipiridis
* Licensed under the MIT License
*
* https://github.com/lipis/bootstrap-social
*/
$bs-height-base: ($line-height-computed + $padding-base-vertical * 2);
$bs-height-lg: (floor($font-size-large * $line-height-base) + $padding-large-vertical * 2);
$bs-height-sm: (floor($font-size-small * 1.5) + $padding-small-vertical * 2);
$bs-height-xs: (floor($font-size-small * 1.2) + $padding-small-vertical + 1);
.btn-social {
position: relative;
padding-left: ($bs-height-base + $padding-base-horizontal);
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
> :first-child {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: $bs-height-base;
line-height: ($bs-height-base + 2);
font-size: 1.6em;
text-align: center;
border-right: 1px solid rgba(0, 0, 0, 0.2);
}
&.btn-lg {
padding-left: ($bs-height-lg + $padding-large-horizontal);
:first-child {
line-height: $bs-height-lg;
width: $bs-height-lg;
font-size: 1.8em;
}
}
&.btn-sm {
padding-left: ($bs-height-sm + $padding-small-horizontal);
:first-child {
line-height: $bs-height-sm;
width: $bs-height-sm;
font-size: 1.4em;
}
}
&.btn-xs {
padding-left: ($bs-height-xs + $padding-small-horizontal);
:first-child {
line-height: $bs-height-xs;
width: $bs-height-xs;
font-size: 1.2em;
}
}
}
.btn-social-icon {
@extend .btn-social;
height: ($bs-height-base + 2);
width: ($bs-height-base + 2);
padding: 0;
:first-child {
border: none;
text-align: center;
width: 100%!important;
}
&.btn-lg {
height: $bs-height-lg;
width: $bs-height-lg;
padding-left: 0;
padding-right: 0;
}
&.btn-sm {
height: ($bs-height-sm + 2);
width: ($bs-height-sm + 2);
padding-left: 0;
padding-right: 0;
}
&.btn-xs {
height: ($bs-height-xs + 2);
width: ($bs-height-xs + 2);
padding-left: 0;
padding-right: 0;
}
}
@mixin btn-social($color-bg, $color: #fff) {
background-color: $color-bg;
@include button-variant($color, $color-bg, rgba(0,0,0,.2));
}
.btn-adn { @include btn-social(#d87a68); }
.btn-bitbucket { @include btn-social(#205081); }
.btn-dropbox { @include btn-social(#1087dd); }
.btn-facebook { @include btn-social(#3b5998); }
.btn-flickr { @include btn-social(#ff0084); }
.btn-foursquare { @include btn-social(#f94877); }
.btn-github { @include btn-social(#444444); }
.btn-google-plus { @include btn-social(#dd4b39); }
.btn-instagram { @include btn-social(#3f729b); }
.btn-linkedin { @include btn-social(#007bb6); }
.btn-microsoft { @include btn-social(#2672ec); }
.btn-openid { @include btn-social(#f7931e); }
.btn-pinterest { @include btn-social(#cb2027); }
.btn-reddit { @include btn-social(#eff7ff, #000); }
.btn-soundcloud { @include btn-social(#ff5500); }
.btn-tumblr { @include btn-social(#2c4762); }
.btn-twitter { @include btn-social(#55acee); }
.btn-vimeo { @include btn-social(#1ab7ea); }
.btn-vk { @include btn-social(#587ea3); }
.btn-yahoo { @include btn-social(#720e9e); }
This diff is collapsed.
/*
Copyright 2014 Roland Bouman
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
body {
background-color: white;
font-family: Arial, sans;
}
pre, pre *, code, textarea {
font-family: Courier, monospace !important;
}
legend {
font-weight: bold;
}
h1 {
background-image: url(logo80x58.png);
background-repeat:no-repeat;
padding-left: 90px;
height: 70px;
}
This diff is collapsed.
This diff is collapsed.
/*!
DataTables Bootstrap 3 integration
©2011-2014 SpryMedia Ltd - datatables.net/license
*/
(function(){var f=function(c,b){c.extend(!0,b.defaults,{dom:"<'row'<'col-sm-6'l><'col-sm-6'f>><'row'<'col-sm-12'tr>><'row'<'col-sm-6'i><'col-sm-6'p>>",renderer:"bootstrap"});c.extend(b.ext.classes,{sWrapper:"dataTables_wrapper form-inline dt-bootstrap",sFilterInput:"form-control input-sm",sLengthSelect:"form-control input-sm"});b.ext.renderer.pageButton.bootstrap=function(g,f,p,k,h,l){var q=new b.Api(g),r=g.oClasses,i=g.oLanguage.oPaginate,d,e,o=function(b,f){var j,m,n,a,k=function(a){a.preventDefault();
c(a.currentTarget).hasClass("disabled")||q.page(a.data.action).draw(!1)};j=0;for(m=f.length;j<m;j++)if(a=f[j],c.isArray(a))o(b,a);else{e=d="";switch(a){case "ellipsis":d="&hellip;";e="disabled";break;case "first":d=i.sFirst;e=a+(0<h?"":" disabled");break;case "previous":d=i.sPrevious;e=a+(0<h?"":" disabled");break;case "next":d=i.sNext;e=a+(h<l-1?"":" disabled");break;case "last":d=i.sLast;e=a+(h<l-1?"":" disabled");break;default:d=a+1,e=h===a?"active":""}d&&(n=c("<li>",{"class":r.sPageButton+" "+
e,"aria-controls":g.sTableId,tabindex:g.iTabIndex,id:0===p&&"string"===typeof a?g.sTableId+"_"+a:null}).append(c("<a>",{href:"#"}).html(d)).appendTo(b),g.oApi._fnBindAction(n,{action:a},k))}};o(c(f).empty().html('<ul class="pagination"/>').children("ul"),k)};b.TableTools&&(c.extend(!0,b.TableTools.classes,{container:"DTTT btn-group",buttons:{normal:"btn btn-default",disabled:"disabled"},collection:{container:"DTTT_dropdown dropdown-menu",buttons:{normal:"",disabled:"disabled"}},print:{info:"DTTT_print_info"},
select:{row:"active"}}),c.extend(!0,b.TableTools.DEFAULTS.oTags,{collection:{container:"ul",button:"li",liner:"a"}}))};"function"===typeof define&&define.amd?define(["jquery","datatables"],f):"object"===typeof exports?f(require("jquery"),require("datatables")):jQuery&&f(jQuery,jQuery.fn.dataTable)})(window,document);
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment