Browse Source

Merge branch 'graphs' of steve/monitor into master

Steve L. Nyemba 8 years ago
parent
commit
8d4f895bea
55 changed files with 22814 additions and 1119 deletions
  1. BIN
      .config.json.un~
  2. 1 0
      .gitignore
  3. 7 0
      .idea/inspectionProfiles/profiles_settings.xml
  4. 4 0
      .idea/misc.xml
  5. 8 0
      .idea/modules.xml
  6. 19 0
      .idea/monitor.iml
  7. 6 0
      .idea/vcs.xml
  8. 583 0
      .idea/workspace.xml
  9. 13 5
      config.json
  10. 15 0
      config.json_bak
  11. 35 0
      couchdb/views.js
  12. 67 0
      init.sh
  13. 1 1
      install.sh
  14. 14 0
      readme.md
  15. 9 6
      requirements.txt
  16. BIN
      src/api/.index.py.un~
  17. 359 202
      src/api/index.py
  18. 615 0
      src/api/static/chartist/chartist.css
  19. 10 0
      src/api/static/chartist/chartist.css.map
  20. 4488 0
      src/api/static/chartist/chartist.js
  21. 1 0
      src/api/static/chartist/chartist.min.css
  22. 10 0
      src/api/static/chartist/chartist.min.js
  23. 1 0
      src/api/static/chartist/chartist.min.js.map
  24. 241 0
      src/api/static/chartist/scss/chartist.scss
  25. 88 0
      src/api/static/chartist/scss/settings/_chartist-settings.scss
  26. 6 0
      src/api/static/css/animate.min.css
  27. 5 0
      src/api/static/css/bootstrap.min.css
  28. 2752 0
      src/api/static/css/dashboard.css
  29. 0 146
      src/api/static/css/default.css
  30. 1 1
      src/api/static/css/fa/scss/_core.scss
  31. 1081 0
      src/api/static/css/themify-icons.css
  32. BIN
      src/api/static/fonts/themify.eot
  33. 362 0
      src/api/static/fonts/themify.svg
  34. BIN
      src/api/static/fonts/themify.ttf
  35. BIN
      src/api/static/fonts/themify.woff
  36. 243 0
      src/api/static/img/uml-design
  37. 0 12
      src/api/static/index.html
  38. BIN
      src/api/static/js/.dashboard.js.un~
  39. 248 0
      src/api/static/js/bootstrap-checkbox-radio.js
  40. 404 0
      src/api/static/js/bootstrap-notify.js
  41. 7 0
      src/api/static/js/bootstrap.min.js
  42. 9 0
      src/api/static/js/chartist.min.js
  43. 216 0
      src/api/static/js/chartist.min.js.map
  44. 0 2
      src/api/static/js/colors.js
  45. 180 561
      src/api/static/js/dashboard.js
  46. 140 0
      src/api/static/js/default.js
  47. 9789 0
      src/api/static/js/jquery-1.10.2.js
  48. 401 171
      src/api/templates/dashboard.html
  49. 1 0
      src/api/templates/upgrade.html
  50. 1 0
      src/api/templates/user.html
  51. 4 1
      src/monitor.py
  52. 303 0
      src/utils/agents/actor.py
  53. 23 3
      src/utils/agents/data-collector.py
  54. 35 8
      src/utils/transport.py
  55. 8 0
      start.sh

BIN
.config.json.un~


+ 1 - 0
.gitignore

@@ -1,2 +1,3 @@
 sandbox
 sandbox
 *.pyc
 *.pyc
+.DS_Store

+ 7 - 0
.idea/inspectionProfiles/profiles_settings.xml

@@ -0,0 +1,7 @@
+<component name="InspectionProjectProfileManager">
+  <settings>
+    <option name="useProjectProfile" value="false" />
+    <option name="USE_PROJECT_PROFILE" value="false" />
+    <version value="1.0" />
+  </settings>
+</component>

+ 4 - 0
.idea/misc.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectRootManager" version="2" project-jdk-name="Python 2.7.10 virtualenv at ~/Documents/Programming/monitor/sandbox" project-jdk-type="Python SDK" />
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/monitor.iml" filepath="$PROJECT_DIR$/.idea/monitor.iml" />
+    </modules>
+  </component>
+</project>

+ 19 - 0
.idea/monitor.iml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="PYTHON_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+  <component name="TemplatesService">
+    <option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
+    <option name="TEMPLATE_FOLDERS">
+      <list>
+        <option value="$MODULE_DIR$/src/api/templates" />
+      </list>
+    </option>
+  </component>
+  <component name="TestRunnerService">
+    <option name="PROJECT_TEST_RUNNER" value="Unittests" />
+  </component>
+</module>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>

+ 583 - 0
.idea/workspace.xml

@@ -0,0 +1,583 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ChangeListManager">
+    <list default="true" id="834735bc-31bd-474a-9b31-c9bb58743d7d" name="Default" comment="">
+      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/config.json" afterPath="$PROJECT_DIR$/config.json" />
+    </list>
+    <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
+    <option name="TRACKING_ENABLED" value="true" />
+    <option name="SHOW_DIALOG" value="false" />
+    <option name="HIGHLIGHT_CONFLICTS" value="true" />
+    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
+    <option name="LAST_RESOLUTION" value="IGNORE" />
+  </component>
+  <component name="CreatePatchCommitExecutor">
+    <option name="PATCH_PATH" value="" />
+  </component>
+  <component name="ExecutionTargetManager" SELECTED_TARGET="default_target" />
+  <component name="FileEditorManager">
+    <splitter split-orientation="horizontal" split-proportion="0.54361874">
+      <split-first>
+        <leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
+          <file leaf-file-name="dashboard.html" pinned="false" current-in-tab="false">
+            <entry file="file://$PROJECT_DIR$/src/api/templates/dashboard.html">
+              <provider selected="true" editor-type-id="text-editor">
+                <state relative-caret-position="213">
+                  <caret line="65" column="5" lean-forward="true" selection-start-line="65" selection-start-column="5" selection-end-line="65" selection-end-column="5" />
+                  <folding>
+                    <element signature="e#2414#2713#0#HTML" expanded="false" />
+                  </folding>
+                </state>
+              </provider>
+            </entry>
+          </file>
+          <file leaf-file-name="dashboard.js" pinned="false" current-in-tab="true">
+            <entry file="file://$PROJECT_DIR$/src/api/static/js/dashboard.js">
+              <provider selected="true" editor-type-id="text-editor">
+                <state relative-caret-position="226">
+                  <caret line="26" column="0" lean-forward="true" selection-start-line="26" selection-start-column="0" selection-end-line="26" selection-end-column="0" />
+                  <folding>
+                    <marker date="1499136933000" expanded="true" signature="63:670" ph="{...}" />
+                    <marker date="1499136933000" expanded="true" signature="638:670" ph="{...}" />
+                  </folding>
+                </state>
+              </provider>
+            </entry>
+          </file>
+        </leaf>
+      </split-first>
+      <split-second>
+        <leaf>
+          <file leaf-file-name="index.py" pinned="false" current-in-tab="true">
+            <entry file="file://$PROJECT_DIR$/src/api/index.py">
+              <provider selected="true" editor-type-id="text-editor">
+                <state relative-caret-position="259">
+                  <caret line="81" column="15" lean-forward="false" selection-start-line="81" selection-start-column="14" selection-end-line="81" selection-end-column="15" />
+                  <folding />
+                </state>
+              </provider>
+            </entry>
+          </file>
+          <file leaf-file-name="config.json" pinned="false" current-in-tab="false">
+            <entry file="file://$PROJECT_DIR$/config.json">
+              <provider selected="true" editor-type-id="text-editor">
+                <state relative-caret-position="270">
+                  <caret line="18" column="0" lean-forward="true" selection-start-line="18" selection-start-column="0" selection-end-line="18" selection-end-column="0" />
+                  <folding />
+                </state>
+              </provider>
+            </entry>
+          </file>
+        </leaf>
+      </split-second>
+    </splitter>
+  </component>
+  <component name="FindInProjectRecents">
+    <findStrings>
+      <find>nodes</find>
+      <find>console.log</find>
+    </findStrings>
+  </component>
+  <component name="Git.Settings">
+    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
+  </component>
+  <component name="IdeDocumentHistory">
+    <option name="CHANGED_PATHS">
+      <list>
+        <option value="$PROJECT_DIR$/src/api/index.py" />
+        <option value="$PROJECT_DIR$/config.json" />
+        <option value="$PROJECT_DIR$/src/api/templates/dashboard.html" />
+        <option value="$PROJECT_DIR$/src/api/static/js/dashboard.js" />
+      </list>
+    </option>
+  </component>
+  <component name="JsBuildToolGruntFileManager" detection-done="true" sorting="DEFINITION_ORDER" />
+  <component name="JsBuildToolPackageJson" detection-done="true" sorting="DEFINITION_ORDER" />
+  <component name="JsGulpfileManager">
+    <detection-done>true</detection-done>
+    <sorting>DEFINITION_ORDER</sorting>
+  </component>
+  <component name="ProjectFrameBounds">
+    <option name="y" value="22" />
+    <option name="width" value="1280" />
+    <option name="height" value="774" />
+  </component>
+  <component name="ProjectView">
+    <navigator currentView="ProjectPane" proportions="" version="1">
+      <flattenPackages />
+      <showMembers />
+      <showModules />
+      <showLibraryContents />
+      <hideEmptyPackages />
+      <abbreviatePackageNames />
+      <autoscrollToSource />
+      <autoscrollFromSource />
+      <sortByType />
+      <manualOrder />
+      <foldersAlwaysOnTop value="true" />
+    </navigator>
+    <panes>
+      <pane id="ProjectPane">
+        <subPane>
+          <PATH>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="monitor" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="monitor" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+          </PATH>
+          <PATH>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="monitor" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="monitor" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="src" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+          </PATH>
+          <PATH>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="monitor" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="monitor" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="src" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="api" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+          </PATH>
+          <PATH>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="monitor" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="monitor" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="src" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="api" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="templates" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+          </PATH>
+          <PATH>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="monitor" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="monitor" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="src" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="api" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="static" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="js" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+          </PATH>
+          <PATH>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="monitor" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="monitor" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="src" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="api" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+            <PATH_ELEMENT>
+              <option name="myItemId" value="static" />
+              <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" />
+            </PATH_ELEMENT>
+          </PATH>
+        </subPane>
+      </pane>
+      <pane id="Scope" />
+      <pane id="Scratches" />
+    </panes>
+  </component>
+  <component name="PropertiesComponent">
+    <property name="WebServerToolWindowFactoryState" value="false" />
+    <property name="settings.editor.selected.configurable" value="vcs.Git" />
+  </component>
+  <component name="RunDashboard">
+    <option name="ruleStates">
+      <list>
+        <RuleState>
+          <option name="name" value="ConfigurationTypeDashboardGroupingRule" />
+        </RuleState>
+        <RuleState>
+          <option name="name" value="StatusDashboardGroupingRule" />
+        </RuleState>
+      </list>
+    </option>
+  </component>
+  <component name="RunManager">
+    <configuration default="true" type="BashConfigurationType" factoryName="Bash">
+      <option name="INTERPRETER_OPTIONS" value="" />
+      <option name="INTERPRETER_PATH" value="/bin/bash" />
+      <option name="WORKING_DIRECTORY" value="" />
+      <option name="PARENT_ENVS" value="true" />
+      <option name="SCRIPT_NAME" value="" />
+      <option name="PARAMETERS" value="" />
+      <module name="" />
+      <envs />
+      <method />
+    </configuration>
+    <configuration default="true" type="DjangoTestsConfigurationType" factoryName="Django tests">
+      <option name="INTERPRETER_OPTIONS" value="" />
+      <option name="PARENT_ENVS" value="true" />
+      <envs>
+        <env name="PYTHONUNBUFFERED" value="1" />
+      </envs>
+      <option name="SDK_HOME" value="" />
+      <option name="WORKING_DIRECTORY" value="" />
+      <option name="IS_MODULE_SDK" value="false" />
+      <option name="ADD_CONTENT_ROOTS" value="true" />
+      <option name="ADD_SOURCE_ROOTS" value="true" />
+      <module name="monitor" />
+      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
+      <option name="TARGET" value="" />
+      <option name="SETTINGS_FILE" value="" />
+      <option name="CUSTOM_SETTINGS" value="false" />
+      <option name="USE_OPTIONS" value="false" />
+      <option name="OPTIONS" value="" />
+      <method />
+    </configuration>
+    <configuration default="true" type="JavaScriptTestRunnerJest" factoryName="Jest">
+      <node-interpreter value="project" />
+      <working-dir value="" />
+      <envs />
+      <scope-kind value="ALL" />
+      <method />
+    </configuration>
+    <configuration default="true" type="JavaScriptTestRunnerProtractor" factoryName="Protractor">
+      <config-file value="" />
+      <node-interpreter value="project" />
+      <envs />
+      <method />
+    </configuration>
+    <configuration default="true" type="JavascriptDebugType" factoryName="JavaScript Debug">
+      <method />
+    </configuration>
+    <configuration default="true" type="PyBehaveRunConfigurationType" factoryName="Behave">
+      <option name="INTERPRETER_OPTIONS" value="" />
+      <option name="PARENT_ENVS" value="true" />
+      <envs />
+      <option name="SDK_HOME" value="" />
+      <option name="WORKING_DIRECTORY" value="" />
+      <option name="IS_MODULE_SDK" value="false" />
+      <option name="ADD_CONTENT_ROOTS" value="true" />
+      <option name="ADD_SOURCE_ROOTS" value="true" />
+      <module name="monitor" />
+      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
+      <option name="ADDITIONAL_ARGS" value="" />
+      <method />
+    </configuration>
+    <configuration default="true" type="PyLettuceRunConfigurationType" factoryName="Lettuce">
+      <option name="INTERPRETER_OPTIONS" value="" />
+      <option name="PARENT_ENVS" value="true" />
+      <envs />
+      <option name="SDK_HOME" value="" />
+      <option name="WORKING_DIRECTORY" value="" />
+      <option name="IS_MODULE_SDK" value="false" />
+      <option name="ADD_CONTENT_ROOTS" value="true" />
+      <option name="ADD_SOURCE_ROOTS" value="true" />
+      <module name="monitor" />
+      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
+      <option name="ADDITIONAL_ARGS" value="" />
+      <method />
+    </configuration>
+    <configuration default="true" type="PythonConfigurationType" factoryName="Python">
+      <option name="INTERPRETER_OPTIONS" value="" />
+      <option name="PARENT_ENVS" value="true" />
+      <envs>
+        <env name="PYTHONUNBUFFERED" value="1" />
+      </envs>
+      <option name="SDK_HOME" value="" />
+      <option name="WORKING_DIRECTORY" value="" />
+      <option name="IS_MODULE_SDK" value="false" />
+      <option name="ADD_CONTENT_ROOTS" value="true" />
+      <option name="ADD_SOURCE_ROOTS" value="true" />
+      <module name="monitor" />
+      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
+      <option name="SCRIPT_NAME" value="" />
+      <option name="PARAMETERS" value="" />
+      <option name="SHOW_COMMAND_LINE" value="false" />
+      <option name="EMULATE_TERMINAL" value="false" />
+      <method />
+    </configuration>
+    <configuration default="true" type="Tox" factoryName="Tox">
+      <option name="INTERPRETER_OPTIONS" value="" />
+      <option name="PARENT_ENVS" value="true" />
+      <envs />
+      <option name="SDK_HOME" value="" />
+      <option name="WORKING_DIRECTORY" value="" />
+      <option name="IS_MODULE_SDK" value="false" />
+      <option name="ADD_CONTENT_ROOTS" value="true" />
+      <option name="ADD_SOURCE_ROOTS" value="true" />
+      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
+      <module name="monitor" />
+      <method />
+    </configuration>
+    <configuration default="true" type="js.build_tools.gulp" factoryName="Gulp.js">
+      <method />
+    </configuration>
+    <configuration default="true" type="js.build_tools.npm" factoryName="npm">
+      <command value="run" />
+      <scripts />
+      <node-interpreter value="project" />
+      <envs />
+      <method />
+    </configuration>
+    <configuration default="true" type="tests" factoryName="Doctests">
+      <option name="INTERPRETER_OPTIONS" value="" />
+      <option name="PARENT_ENVS" value="true" />
+      <envs />
+      <option name="SDK_HOME" value="" />
+      <option name="WORKING_DIRECTORY" value="" />
+      <option name="IS_MODULE_SDK" value="false" />
+      <option name="ADD_CONTENT_ROOTS" value="true" />
+      <option name="ADD_SOURCE_ROOTS" value="true" />
+      <module name="monitor" />
+      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
+      <option name="SCRIPT_NAME" value="" />
+      <option name="CLASS_NAME" value="" />
+      <option name="METHOD_NAME" value="" />
+      <option name="FOLDER_NAME" value="" />
+      <option name="TEST_TYPE" value="TEST_SCRIPT" />
+      <option name="PATTERN" value="" />
+      <option name="USE_PATTERN" value="false" />
+      <method />
+    </configuration>
+    <configuration default="true" type="tests" factoryName="Unittests">
+      <option name="INTERPRETER_OPTIONS" value="" />
+      <option name="PARENT_ENVS" value="true" />
+      <envs />
+      <option name="SDK_HOME" value="" />
+      <option name="WORKING_DIRECTORY" value="" />
+      <option name="IS_MODULE_SDK" value="false" />
+      <option name="ADD_CONTENT_ROOTS" value="true" />
+      <option name="ADD_SOURCE_ROOTS" value="true" />
+      <module name="monitor" />
+      <EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" />
+      <option name="_new_additionalArguments" value="&quot;&quot;" />
+      <option name="_new_target" value="&quot;.&quot;" />
+      <option name="_new_targetType" value="&quot;PATH&quot;" />
+      <method />
+    </configuration>
+  </component>
+  <component name="ShelveChangesManager" show_recycled="false">
+    <option name="remove_strategy" value="false" />
+  </component>
+  <component name="TaskManager">
+    <task active="true" id="Default" summary="Default task">
+      <changelist id="834735bc-31bd-474a-9b31-c9bb58743d7d" name="Default" comment="" />
+      <created>1499042529793</created>
+      <option name="number" value="Default" />
+      <option name="presentableId" value="Default" />
+      <updated>1499042529793</updated>
+    </task>
+    <servers />
+  </component>
+  <component name="ToolWindowManager">
+    <frame x="0" y="22" width="1280" height="774" extended-state="6" />
+    <editor active="true" />
+    <layout>
+      <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
+      <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="7" side_tool="true" content_ui="tabs" />
+      <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32991204" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
+      <window_info id="Python Console" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
+      <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
+      <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.32991204" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
+      <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.15347335" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
+      <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
+      <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32991204" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
+      <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
+      <window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="true" content_ui="tabs" />
+      <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
+      <window_info id="Data View" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
+      <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
+      <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
+      <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
+      <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
+      <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
+      <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
+    </layout>
+  </component>
+  <component name="TypeScriptGeneratedFilesManager">
+    <option name="processedProjectFiles" value="true" />
+  </component>
+  <component name="VcsContentAnnotationSettings">
+    <option name="myLimit" value="2678400000" />
+  </component>
+  <component name="XDebuggerManager">
+    <breakpoint-manager />
+    <watches-manager />
+  </component>
+  <component name="editorHistoryManager">
+    <entry file="file://$PROJECT_DIR$/src/api/index.py">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="0">
+          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/src/api/templates/dashboard.html">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="855">
+          <caret line="57" column="17" lean-forward="false" selection-start-line="57" selection-start-column="16" selection-end-line="57" selection-end-column="17" />
+          <folding>
+            <element signature="e#2414#2713#0#HTML" expanded="false" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/config.json">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="0">
+          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/start.sh">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="120">
+          <caret line="8" column="0" lean-forward="false" selection-start-line="8" selection-start-column="0" selection-end-line="8" selection-end-column="0" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/config.json">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="0">
+          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/start.sh">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="0">
+          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/config.json">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="0">
+          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/start.sh">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="0">
+          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/config.json">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="0">
+          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/start.sh">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="0">
+          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/start.sh">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="120">
+          <caret line="8" column="0" lean-forward="false" selection-start-line="8" selection-start-column="0" selection-end-line="8" selection-end-column="0" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/config.json">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="270">
+          <caret line="18" column="0" lean-forward="true" selection-start-line="18" selection-start-column="0" selection-end-line="18" selection-end-column="0" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/src/api/index.py">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="259">
+          <caret line="81" column="15" lean-forward="false" selection-start-line="81" selection-start-column="14" selection-end-line="81" selection-end-column="15" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/src/api/templates/dashboard.html">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="213">
+          <caret line="65" column="5" lean-forward="true" selection-start-line="65" selection-start-column="5" selection-end-line="65" selection-end-column="5" />
+          <folding>
+            <element signature="e#2414#2713#0#HTML" expanded="false" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/src/api/static/js/dashboard.js">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="226">
+          <caret line="26" column="0" lean-forward="true" selection-start-line="26" selection-start-column="0" selection-end-line="26" selection-end-column="0" />
+          <folding>
+            <marker date="1499136933000" expanded="true" signature="63:670" ph="{...}" />
+            <marker date="1499136933000" expanded="true" signature="638:670" ph="{...}" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+  </component>
+</project>

+ 13 - 5
config.json

@@ -1,10 +1,18 @@
 {
 {
-	"id":"lab",
+	"id":"michaels-MBP",
+	"key":"4q-h8r5-247&!570p=[0v8]x360",
+	"api":"dev.the-phi.com",
+	"delay":10.0,
 	"store":{
 	"store":{
 		"class":{"read":"CouchdbReader","write":"CouchdbWriter"},
 		"class":{"read":"CouchdbReader","write":"CouchdbWriter"},
-		"args":{"uri":"http://dev.the-phi.com:5984","dbname":"mike-db","uid":"log_linode"}
+		"args":{"uri":"http://dev.the-phi.com:5984","dbname":"mike-db","uid":"logs"}
 	},
 	},
-	"procs":["systemd", "rcu_sched"],
-	"folders":["~/monitor/monitor"],
-	"delay":2
+	"procs":["mail","safari", "chrome", "terminal"],
+	"folders":["/Users/michaelmead/Downloads"],
+	"actions":{
+		"folders":{"threshold":"10mb","action":"archive","key":""},
+		"apps":{"mail":"","safari":"","chrome":""	}
+	}
+
+
 }
 }

+ 15 - 0
config.json_bak

@@ -0,0 +1,15 @@
+{
+	"virtual-env":{
+		"class":"Sandbox",
+		"config":{
+			"3-launchpad":{"requirements":"/Users/michaelmead/Documents/git/repair-file/required.txt","sandbox":"/Users/michaelmead/Documents/git/sandbox"}
+		}
+	},
+	"processes":{
+		"class":"DetailProcess",
+		"config":{
+			"system":["postgresql","couchdb","httpd"]
+			}
+	}
+	
+}

File diff suppressed because it is too large
+ 35 - 0
couchdb/views.js


+ 67 - 0
init.sh

@@ -0,0 +1,67 @@
+#!/bin/bash
+#
+# This script is designed to handle various operations related to setting up, starting, stopping the python application
+# It will assume the requirements file is at the root (not with the source code)
+#
+
+export PYTHONPATH=$PWD/src
+pip_upgrade='sandbox/bin/pip freeze|sort |diff requirements.txt -|grep \<|grep -E " .+$" -o'
+install(){
+	
+	virtualenv sandbox
+	sandbox/bin/pip install -r requirements.txt
+	`sandbox/bin/pip freeze|sort |diff requirements.txt -|grep \<|grep -E " .+$" -o|sandbox/bin/pip install --upgrade` 
+
+
+}
+upgrade(){
+	git pull
+	count=`sandbox/bin/pip freeze|sort |diff requirements.txt -|grep \<|grep -E " .+$" -o|wc -l`
+	if [ ! "$count" = "0" ]; then
+	 	`sandbox/bin/pip freeze|sort |diff requirements.txt -|grep \<|grep -E " .+$" -o|sandbox/bin/pip install --upgrade`
+	 else
+	 	echo "No Upgrade required for sandbox"
+	 fi
+	
+}
+start(){
+	
+	if [ "$1" = "collector" ]; then
+		sandbox/bin/python src/utils/agents/data-collector.py --path $PWD/config.json
+	else
+
+		sandbox/bin/python src/api/index.py --path $PWD/config.json &
+	fi
+
+}
+stop(){
+	ps -eo pid,command|grep python|grep -E "$PWD"|grep index.py|grep -E "^ {0,}[0-9]+" -o |xargs kill -9
+	ps -eo pid,command|grep python|grep -E "$PWD"|grep data-collector|grep -E "^ {0,}[0-9]+" -o |xargs kill -9
+}
+
+status(){
+	pid=`ps -eo pid,command|grep python|grep -E "$PWD"|grep index.py|grep -E "^ {0,}[0-9]+" -m 1 -o`
+	if [ "$pid" = "" ]; then
+		echo "API IS OFFLINE"
+	else
+		echo "API IS ONLINE $pid"
+	fi
+	pid=`ps -eo pid,command|grep python|grep -E "$PWD"|grep data-collector|grep -E "^ {0,}[0-9]+" -m 1 -o`
+	if [ "$pid" = "" ]; then 
+		echo "DATA-COLLECTOR IS OFFLINE"
+	else
+		echo "DATA-COLLECTOR IS ONLINE $pid"
+	fi
+
+}
+
+if [ "$1" = "start" ]; then
+	if [ "$2" = "collector" ]; then
+		start "collector"
+	else
+		start
+	fi
+else
+	$1
+
+fi

+ 1 - 1
install.sh

@@ -1,7 +1,7 @@
 #!/bin/bash
 #!/bin/bash
 
 
 git clone https://github.com/lnyemba/jx src/api/static/js/jx
 git clone https://github.com/lnyemba/jx src/api/static/js/jx
-virtualenv sandbox
+virtualenv sandbox --python=python2.7
 source sandbox/bin/activate
 source sandbox/bin/activate
 pip install -r requirements.txt
 pip install -r requirements.txt
 export PYTHONPATH=$PWD/src
 export PYTHONPATH=$PWD/src

+ 14 - 0
readme.md

@@ -6,6 +6,20 @@ The program answers basic questions:
 	- Is a given program still running
 	- Is a given program still running
 	- How much resource (memory/cpu) a program is using up
 	- How much resource (memory/cpu) a program is using up
 	- The number of processes found
 	- The number of processes found
+	- Folder monitoring ...
+	
+#Architecture
+
+The architecture of the system is distributed with a central master node,
+    {
+        "id":"",
+        "key":"",
+        "apps":[],
+        "sandbox":[{"path":"","requirements":""}],
+        "folders":["path-1"],
+        "store":{}
+        "actions":{}
+    }
 
 
 The agent will perform three basic functions :
 The agent will perform three basic functions :
 
 

+ 9 - 6
requirements.txt

@@ -1,13 +1,16 @@
-aniso8601==1.2.0
-click==6.6
-couchdbkit==0.6.5
 Flask==0.11.1
 Flask==0.11.1
 Flask-Session==0.3.0
 Flask-Session==0.3.0
 Flask-SocketIO==2.8.2
 Flask-SocketIO==2.8.2
-http-parser==0.8.3
-itsdangerous==0.24
 Jinja2==2.8
 Jinja2==2.8
 MarkupSafe==0.23
 MarkupSafe==0.23
+Werkzeug==0.11.11
+aniso8601==1.2.0
+argparse==1.2.1
+click==6.6
+couchdbkit==0.6.5
+http-parser==0.8.3
+itsdangerous==0.24
+ngram==3.3.0
 numpy==1.11.3
 numpy==1.11.3
 pika==0.10.0
 pika==0.10.0
 python-dateutil==2.6.0
 python-dateutil==2.6.0
@@ -17,4 +20,4 @@ pytz==2016.10
 restkit==4.2.2
 restkit==4.2.2
 six==1.10.0
 six==1.10.0
 socketpool==0.5.3
 socketpool==0.5.3
-Werkzeug==0.11.11
+wsgiref==0.1.2

BIN
src/api/.index.py.un~


+ 359 - 202
src/api/index.py

@@ -1,13 +1,16 @@
 """
 """
 	This is a RESTful interface implemented using Flask micro framework.
 	This is a RESTful interface implemented using Flask micro framework.
 	The API is driven by configuration that is organized in terms of the monitoring classes
 	The API is driven by configuration that is organized in terms of the monitoring classes
-	
+
 	The API is both restful and websocket/socketio enabled.
 	The API is both restful and websocket/socketio enabled.
 
 
 	We designed the classes to be reusable (and powered by labels):
 	We designed the classes to be reusable (and powered by labels):
 	'monitoring-type':
 	'monitoring-type':
 		'class':'<class-name>'
 		'class':'<class-name>'
 		'config':<labeled-class-specific-configuration>'
 		'config':<labeled-class-specific-configuration>'
+	@TODO:
+		- In order to make this Saas we need to have the configuration be session driven
+		- Add socketio, so that each section of the dashboard updates independently
 """
 """
 
 
 from flask import Flask, session, request, redirect, Response
 from flask import Flask, session, request, redirect, Response
@@ -45,51 +48,180 @@ class_write= CONFIG['store']['class']['write']
 factory = DataSourceFactory()
 factory = DataSourceFactory()
 # gReader = factory.instance(type=class_read,args=p)
 # gReader = factory.instance(type=class_read,args=p)
 
 
-atexit.register(ThreadManager.stop)
-@app.route('/get/<id>')
-def procs(id):	
+@app.route('/')
+def home():
+	context = PARAMS['context']
+	if 'title' in PARAMS :
+		title = PARAMS['title']
+	else:
+		title = 'Dashboard'
+	apps = []
 	try:
 	try:
 		gReader = factory.instance(type=class_read,args=p)
 		gReader = factory.instance(type=class_read,args=p)
-		data =  gReader.read()
-		ahandler = AnalyzeAnomaly()
-		learn = {}
-		if 'learn' in data :
-			for row in data['learn'] :
-				label = row['label']
-				learn[label] = row
-		r = {}
-		for label in data :
-			if label not in ['learn','folders'] :
-				index = len(data[label]) - 1
-				row = data[label][index]
-				r[label] = row
-				#
-				# Let us determine if this is a normal operation or not
-				# We will update the status of the information ...
-				#
-				
-				for row in r[label] :	
-					index = r[label].index(row)			
-					if row['label'] in learn:
-						id = row['label']
-						px = ahandler.predict([row],learn[id])
-						if px :
-							
-							# row['anomaly'] = px[1]==1
-							print ""
-							print label,' *** ',index
-							row = dict(row,**px)
-							r[label][index] =row
-							#
-							# @TODO:
-							# Compile a report here that will be sent to the mailing list
-							# 
-					
+		apps = gReader.view('summary/nodes',key=p['uid'])
+
 	except Exception, e:
 	except Exception, e:
+		print (e)
+	return render_template('dashboard.html',context=context,title=title,app_names=apps)
+
+@app.route('/1/get/nodes')
+def get_nodes():
+	"""
+		This function returns the labels of applications for every node registered
+		@param None
+	"""
+	r = []
+	try:
+		gReader = factory.instance(type=class_read,args=p)
+		r =  gReader.view('summary/nodes',key=p['uid'])
+	except Exception,e:
+		print (e)
+	return json.dumps(r)
+
+@app.route('/1/get/apps')
+def get_apps():
+	"""
+		This function returns the applications for a given node
+		@param node identifier e.g: apps@zulu.org 								<--------------------no it doesnt
+	"""
+	r = []
+	try:
+		node_id = request.args.get('node')
+		gReader = factory.instance(type=class_read,args=p)
+		r =  gReader.view('summary/app_names',key=p['uid'])
+		r = r[node_id]
+		print r
+	except Exception,e:
+		print (e)
+	return json.dumps(r)
+
+
+
+@app.route('/1/get/summary/<id>')
+def get_summary(id):
+	"""
+		This function returns the summary i.e an overall assessment of resource usage
+		It will pull information out of the user's data-store (database & document) specified in the configuration
+		@param id	{app_resources|app_status|folder_size}
+	"""
+	r = []
+	try:
+		gReader = factory.instance(type=class_read,args=p)
+		#if id == 'apps_resources' :
+		#	r = gReader.view('summary/app_resources',key=p['uid'])
+		#else:
+		#	r = gReader.view('summary/folder_size',key=p['uid'])
+		id='summary/'+id.strip()
+		print p
+		print id
+		r = gReader.view(id,key=p['uid'])
+
+	except Exception,e:
+		print (e)
+	return json.dumps(r)
+@app.route("/1/sys/usage/trend")
+def get_usage_trend():
+	"""
+
+		This function returns cpu/memory usage for the entire system being monitored. It will return the 24 most recent observations in the logs
+		@param 	None
+		@return {memory_usage:[],cpu_usage:[],app_count:value,memory_available:[]}
+	"""
+	r = {}
+	try:
+		gReader = factory.instance(type=class_read,args=p)
+		r = gReader.view('summary/resource_usage_trend',key=p['uid'])
+	except Exception,e:
+		print (e)
+	return json.dumps(r)
+
+@app.route("/1/app/usage/trend")
+def get_usage_detail():
+	"""
+		This function returns detailed information about usage per application monitored. It will return the 24 most recent observations in the logs
+
+		@param node	node identifier e.g: apps@zulu.io
+		@return {node_x:{app_1:{memory_usage:[],cpu_usage:[]}},...}
+	"""
+	r = {}
+	try:
+		id 	= request.args.get('node')
+		app_id	= request.args.get('app')
+		gReader = factory.instance(type=class_read,args=p)
+		r = gReader.view('summary/app_resource_usage_details',key=p['uid'])
+		r = r[id][app_id]
+	except Exception,e:
+		print (e)
+	return json.dumps(r)
+@app.route('/1/app/status')
+def app_status() :
+    	"""
+		This function aggregates the number of crashes/running/idle instances found in the past 24 log entries
+		for a particular application
+		@param nid	node identifier e.g: app@zulu.io
+		@param app	application identifier e.g: kate, firefox, chrome ... specified in the configuraiton
+	"""
+	r = []
+    	try:
+		nid = request.args.get('node') # Node identifier
+		aid = request.args.get('app') # application identifier
+
+		gReader = factory.instance(type=class_read,args=p)
+		r = gReader.view('summary/app_status_details',key=p['uid'])
+		#
+		#@TODO: Once the back-end enables the nodes in which the application is running, uncomment the line below
+		#
+		print[nid,aid]
+		r = r[nid][aid]
+
+	except Exception,e:
 		print e
 		print e
-		r = []
-	
+
 	return json.dumps(r)
 	return json.dumps(r)
+#@app.route('/get/<id>')
+#def procs(id):
+	#try:
+		#gReader = factory.instance(type=class_read,args=p)
+		#data =  gReader.read()
+		#ahandler = AnalyzeAnomaly()
+		#learn = {}
+		#if 'learn' in data :
+			#for row in data['learn'] :
+				#label = row['label']
+				#learn[label] = row
+		#r = {}
+		#for label in data :
+			#if label not in ['learn','folders'] :
+				#index = len(data[label]) - 1
+				#row = data[label][index]
+				#r[label] = row
+				##
+				## Let us determine if this is a normal operation or not
+				## We will update the status of the information ...
+				##
+
+				#for row in r[label] :
+					#index = r[label].index(row)
+					#if row['label'] in learn:
+						#id = row['label']
+						#px = ahandler.predict([row],learn[id])
+						#if px :
+
+							## row['anomaly'] = px[1]==1
+							#print ""
+							#print label,' *** ',index
+							#row = dict(row,**px)
+							#r[label][index] =row
+							##
+							## @TODO:
+							## Compile a report here that will be sent to the mailing list
+							##
+
+	#except Exception, e:
+		#print e
+		#r = []
+
+	#return json.dumps(r)
 
 
 """
 """
 	This function/endpoint will assess n-virtual environments and return the results
 	This function/endpoint will assess n-virtual environments and return the results
@@ -98,7 +230,7 @@ def procs(id):
 @app.route('/sandbox')
 @app.route('/sandbox')
 def sandbox():
 def sandbox():
 	global CONFIG
 	global CONFIG
-	
+
 	if 'sandbox' in CONFIG: #CONFIG['monitor']:
 	if 'sandbox' in CONFIG: #CONFIG['monitor']:
 		#handler = HANDLERS['sandbox']['class']
 		#handler = HANDLERS['sandbox']['class']
 		#conf = HANDLERS['sandbox']['config']
 		#conf = HANDLERS['sandbox']['config']
@@ -106,7 +238,7 @@ def sandbox():
 		# p = Factory.instance('sandbox',CONFIG)
 		# p = Factory.instance('sandbox',CONFIG)
 		handler = monitor.Sandbox()
 		handler = monitor.Sandbox()
 		conf	= CONFIG['sandbox']
 		conf	= CONFIG['sandbox']
-		
+
 		for id in conf:
 		for id in conf:
 			try:
 			try:
 				handler.init(conf[id])
 				handler.init(conf[id])
@@ -114,182 +246,207 @@ def sandbox():
 			except Exception,e:
 			except Exception,e:
 				pass
 				pass
 	else:
 	else:
-		
+
 		r = []
 		r = []
 
 
 
 
 	return json.dumps(r)
 	return json.dumps(r)
-@app.route('/trends') 
-def trends ():
-	id = request.args.get('id')
-	app = request.args.get('app').strip()
-	p = CONFIG['store']['args']
-	class_read = CONFIG['store']['class']['read']
-
-	
-	gReader = factory.instance(type=class_read,args=p)	
-	r = gReader.read()
-	if id in r:
-		r = r[id] #--matrix
-		series = []
-
-		for row in r:
-			
-			series += [item for item in row if str(item['label'])== app]
-		if len(series) > 12 :
-			beg = len(series) - 13
-			series = series[beg:]
-		return json.dumps(series)
-	else:
-		return "[]"
-@app.route('/download',methods=['POST'])
-def requirements():
-	stream = request.form['missing']
-	print stream
-	stream = "\n".join(json.loads(stream))
-	headers = {"content-disposition":"attachment; filename=requirements.txt"}
-	return Response(stream,mimetype='text/plain',headers=headers)
+#@app.route('/trends')
+#def trends ():
+	#id = request.args.get('id')
+	#app = request.args.get('app').strip()
+	#p = CONFIG['store']['args']
+	#class_read = CONFIG['store']['class']['read']
+
+
+	#gReader = factory.instance(type=class_read,args=p)
+	#r = gReader.read()
+	#if id in r:
+		#r = r[id] #--matrix
+		#series = []
+
+		#for row in r:
+
+			#series += [item for item in row if str(item['label'])== app]
+		#if len(series) > 12 :
+			#beg = len(series) - 8
+			#series = series[beg:]
+		#return json.dumps(series)
+	#else:
+		#return "[]"
+#@app.route('/download',methods=['POST'])
+#def requirements():
+	#stream = request.form['missing']
+	#print stream
+	#stream = "\n".join(json.loads(stream))
+	#headers = {"content-disposition":"attachment; filename=requirements.txt"}
+	#return Response(stream,mimetype='text/plain',headers=headers)
+
 @app.route('/dashboard')
 @app.route('/dashboard')
 def dashboard():
 def dashboard():
 	context = PARAMS['context']
 	context = PARAMS['context']
 	if 'title' in PARAMS :
 	if 'title' in PARAMS :
 		title = PARAMS['title']
 		title = PARAMS['title']
 	else:
 	else:
-		title = 'Zulu OverWatch'
-	return render_template('dashboard.html',context=context,title=title)
+		title = 'Dashboard'
+	apps = []
+	try:
+		gReader = factory.instance(type=class_read,args=p)
+		apps = gReader.view('summary/app_names',key=p['uid'])
+	except Exception, e:
+		print (e)
+	return render_template('dashboard.html',context=context,title=title,app_names=apps)
+
+@app.route('/upgrade')
+def upgrade():
+	context = PARAMS['context']
+	if 'title' in PARAMS :
+		title = PARAMS['title']
+	else:
+		title = 'Upgrade'
+	return render_template('upgrade.html',context=context,title=title)
+
+@app.route('/user')
+def user():
+	context = PARAMS['context']
+	if 'title' in PARAMS :
+		title = PARAMS['title']
+	else:
+		title = 'Upgrade'
+	return render_template('user.html',context=context,title=title)
+
+
+#"""
+	#This function is designed to trigger learning for anomaly detection
+	#@TODO: forward this to a socket i.e non-blocking socket
+#"""
+
+#@app.route('/anomalies/get')
+#def learn():
+	#global CONFIG
+	#p = CONFIG['store']['args']
+	#class_read = CONFIG['store']['class']['read']
+	#gReader = factory.instance(type=class_read,args=p)
+	#d =  gReader.read()
+
+	#if 'learn' in d :
+		#info = d['learn']
+
+		#del d['learn']
+	#else :
+		#info = []
+	#r = []
+	#if 'id' in request.args:
+		#id = request.args['id']
+		#d = d[id]
+		#params = {}
+		#for item in info:
+
+			#label = item['label']
+			#params[label] = item
+
+		##apps = list(set(ML.Extract(['label'],d)))
+		#r = []
+		#if params :
+			##
+			## If we have parameters available
+			#p = AnomalyDetection()
+			#apps = params.keys()
+			#for name in apps :
+				#if name not in params:
+					#continue
+				#_info	= params[name]
+				#try:
+					#xo 	= ML.Filter('label',name,d)
+				#except Exception,e:
+					#xo = []
+					##print name,e
+				#if len(xo) == 0:
+					#continue
+				#xo 	= [xo[ len(xo) -1]]
+
+				#value	= p.predict(xo,_info)[0]
+
+				#if len(value):
+					#report = dict(_info,**{'predicton':value})
+					#r.append(report)
+
+
+
+			##print app,value
+			##if value is not None:
+			##	r.append(value)
+
+	#return json.dumps(r)
 
 
-"""
-	This function is designed to trigger learning for anomaly detection
-	@TODO: forward this to a socket i.e non-blocking socket
-"""
-@app.route('/anomalies/get')
-def learn():
-	global CONFIG
-	p = CONFIG['store']['args']
-	class_read = CONFIG['store']['class']['read']	
-	gReader = factory.instance(type=class_read,args=p)
-	d =  gReader.read()
-	
-	if 'learn' in d :
-		info = d['learn']
-		
-		del d['learn']
-	else :
-		info = []
-	r = []
-	if 'id' in request.args:
-		id = request.args['id']
-		d = d[id]
-		params = {}
-		for item in info:
-			
-			label = item['label']
-			params[label] = item
-		
-		#apps = list(set(ML.Extract(['label'],d)))
-		r = []
-		if params :
-			#
-			# If we have parameters available 
-			p = AnomalyDetection()
-			apps = params.keys()			
-			for name in apps :
-				if name not in params:
-					continue
-				_info	= params[name]		
-				try:
-					xo 	= ML.Filter('label',name,d)	
-				except Exception,e:
-					xo = []
-					#print name,e
-				if len(xo) == 0:					
-					continue	
-				xo 	= [xo[ len(xo) -1]]
-				
-				value	= p.predict(xo,_info)[0]
-				
-				if len(value):
-					report = dict(_info,**{'predicton':value})
-					r.append(report)
-				
-				
-				
-			#print app,value
-			#if value is not None:
-			#	r.append(value)
-	
-	return json.dumps(r)
-		
 
 
 """
 """
 	This function returns anomalies for a given context or group of processes
 	This function returns anomalies for a given context or group of processes
 	The information returned is around precision/recall and f-score and parameters
 	The information returned is around precision/recall and f-score and parameters
 """
 """
-@app.route('/anomalies/status')
-def anomalies_status():
-	global CONFIG
-	p = CONFIG['store']['args']
-	class_read = CONFIG['store']['class']['read']	
-	gReader = factory.instance(type=class_read,args=p)
-	d =  gReader.read()
-	if 'learn' in d :
-		info = d['learn']
-		
-		del d['learn']
-	else :
-		info = []
-	print info
-	r = []
-	if 'id' in request.args:
-		id = request.args['id']
-		r = info
-	return json.dumps(r)
-@app.route('/folders')
-def get_folders():
-	global CONFIG
-	p = CONFIG['store']['args']
-	class_read = CONFIG['store']['class']['read']	
-	gReader = factory.instance(type=class_read,args=p)
-	d =  gReader.read()
-	if 'folders' in d:
-		d = d['folders']
-		hosts = set([row[0]['id'] for row in d])
-		m = {}
-		for id in hosts:
-			for row in d:				
-				if id == row[0]['id'] :
-					m[id] = row
-		d = m.values()
-		for row in d:
-			print row[0]['id']
-		# index = len(d) - 1
-		# d = d[index]
-		
-			
-		# m = {}
-		# for row in d :
-			
-		# 	key = row.keys()[0]
-
-		# 	row = row[key]
-		# 	if key not in m:
-		# 		r.append(row)
-		# 		m[key] = len(r) -1
-		# 	else:
-		# 		index = m[key]
-		# 		r[index] = row
-		# d = r
-	else:
-		d = []
-	
-	return json.dumps(d)
-	
+#@app.route('/anomalies/status')
+#def anomalies_status():
+	#global CONFIG
+	#p = CONFIG['store']['args']
+	#class_read = CONFIG['store']['class']['read']
+	#gReader = factory.instance(type=class_read,args=p)
+	#d =  gReader.read()
+	#if 'learn' in d :
+		#info = d['learn']
+
+		#del d['learn']
+	#else :
+		#info = []
+	#print info
+	#r = []
+	#if 'id' in request.args:
+		#id = request.args['id']
+		#r = info
+	#return json.dumps(r)
+#@app.route('/folders')
+#def get_folders():
+	#global CONFIG
+	#p = CONFIG['store']['args']
+	#class_read = CONFIG['store']['class']['read']
+	#gReader = factory.instance(type=class_read,args=p)
+	#d =  gReader.read()
+	#if 'folders' in d:
+		#d = d['folders']
+		#hosts = set([row[0]['id'] for row in d])
+		#m = {}
+		#for id in hosts:
+			#for row in d:
+				#if id == row[0]['id'] :
+					#m[id] = row
+		#d = m.values()
+		#for row in d:
+			#print row[0]['id']
+		## index = len(d) - 1
+		## d = d[index]
+
+
+		## m = {}
+		## for row in d :
+
+		## 	key = row.keys()[0]
+
+		## 	row = row[key]
+		## 	if key not in m:
+		## 		r.append(row)
+		## 		m[key] = len(r) -1
+		## 	else:
+		## 		index = m[key]
+		## 		r[index] = row
+		## d = r
+	#else:
+		#d = []
+
+	#return json.dumps(d)
+
 if __name__== '__main__':
 if __name__== '__main__':
-	
-#	ThreadManager.start(CONFIG)	
+
+#	ThreadManager.start(CONFIG)
 	if 'port' not in SYS_ARGS.PARAMS :
 	if 'port' not in SYS_ARGS.PARAMS :
-		SYS_ARGS.PARAMS['port'] = 5000
+		SYS_ARGS.PARAMS['port'] = 8484
 	PORT = int(SYS_ARGS.PARAMS['port'])
 	PORT = int(SYS_ARGS.PARAMS['port'])
 	app.run(host='0.0.0.0' ,port=PORT,debug=True,threaded=True)
 	app.run(host='0.0.0.0' ,port=PORT,debug=True,threaded=True)
-
-	

+ 615 - 0
src/api/static/chartist/chartist.css

@@ -0,0 +1,615 @@
+.ct-label {
+  fill: rgba(0, 0, 0, 0.4);
+  color: rgba(0, 0, 0, 0.4);
+  font-size: 0.75rem;
+  line-height: 1; }
+
+.ct-chart-line .ct-label,
+.ct-chart-bar .ct-label {
+  display: block;
+  display: -webkit-box;
+  display: -moz-box;
+  display: -ms-flexbox;
+  display: -webkit-flex;
+  display: flex; }
+
+.ct-chart-pie .ct-label,
+.ct-chart-donut .ct-label {
+  dominant-baseline: central; }
+
+.ct-label.ct-horizontal.ct-start {
+  -webkit-box-align: flex-end;
+  -webkit-align-items: flex-end;
+  -ms-flex-align: flex-end;
+  align-items: flex-end;
+  -webkit-box-pack: flex-start;
+  -webkit-justify-content: flex-start;
+  -ms-flex-pack: flex-start;
+  justify-content: flex-start;
+  text-align: left;
+  text-anchor: start; }
+
+.ct-label.ct-horizontal.ct-end {
+  -webkit-box-align: flex-start;
+  -webkit-align-items: flex-start;
+  -ms-flex-align: flex-start;
+  align-items: flex-start;
+  -webkit-box-pack: flex-start;
+  -webkit-justify-content: flex-start;
+  -ms-flex-pack: flex-start;
+  justify-content: flex-start;
+  text-align: left;
+  text-anchor: start; }
+
+.ct-label.ct-vertical.ct-start {
+  -webkit-box-align: flex-end;
+  -webkit-align-items: flex-end;
+  -ms-flex-align: flex-end;
+  align-items: flex-end;
+  -webkit-box-pack: flex-end;
+  -webkit-justify-content: flex-end;
+  -ms-flex-pack: flex-end;
+  justify-content: flex-end;
+  text-align: right;
+  text-anchor: end; }
+
+.ct-label.ct-vertical.ct-end {
+  -webkit-box-align: flex-end;
+  -webkit-align-items: flex-end;
+  -ms-flex-align: flex-end;
+  align-items: flex-end;
+  -webkit-box-pack: flex-start;
+  -webkit-justify-content: flex-start;
+  -ms-flex-pack: flex-start;
+  justify-content: flex-start;
+  text-align: left;
+  text-anchor: start; }
+
+.ct-chart-bar .ct-label.ct-horizontal.ct-start {
+  -webkit-box-align: flex-end;
+  -webkit-align-items: flex-end;
+  -ms-flex-align: flex-end;
+  align-items: flex-end;
+  -webkit-box-pack: center;
+  -webkit-justify-content: center;
+  -ms-flex-pack: center;
+  justify-content: center;
+  text-align: center;
+  text-anchor: start; }
+
+.ct-chart-bar .ct-label.ct-horizontal.ct-end {
+  -webkit-box-align: flex-start;
+  -webkit-align-items: flex-start;
+  -ms-flex-align: flex-start;
+  align-items: flex-start;
+  -webkit-box-pack: center;
+  -webkit-justify-content: center;
+  -ms-flex-pack: center;
+  justify-content: center;
+  text-align: center;
+  text-anchor: start; }
+
+.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-start {
+  -webkit-box-align: flex-end;
+  -webkit-align-items: flex-end;
+  -ms-flex-align: flex-end;
+  align-items: flex-end;
+  -webkit-box-pack: flex-start;
+  -webkit-justify-content: flex-start;
+  -ms-flex-pack: flex-start;
+  justify-content: flex-start;
+  text-align: left;
+  text-anchor: start; }
+
+.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-end {
+  -webkit-box-align: flex-start;
+  -webkit-align-items: flex-start;
+  -ms-flex-align: flex-start;
+  align-items: flex-start;
+  -webkit-box-pack: flex-start;
+  -webkit-justify-content: flex-start;
+  -ms-flex-pack: flex-start;
+  justify-content: flex-start;
+  text-align: left;
+  text-anchor: start; }
+
+.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-start {
+  -webkit-box-align: center;
+  -webkit-align-items: center;
+  -ms-flex-align: center;
+  align-items: center;
+  -webkit-box-pack: flex-end;
+  -webkit-justify-content: flex-end;
+  -ms-flex-pack: flex-end;
+  justify-content: flex-end;
+  text-align: right;
+  text-anchor: end; }
+
+.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-end {
+  -webkit-box-align: center;
+  -webkit-align-items: center;
+  -ms-flex-align: center;
+  align-items: center;
+  -webkit-box-pack: flex-start;
+  -webkit-justify-content: flex-start;
+  -ms-flex-pack: flex-start;
+  justify-content: flex-start;
+  text-align: left;
+  text-anchor: end; }
+
+.ct-grid {
+  stroke: rgba(0, 0, 0, 0.2);
+  stroke-width: 1px;
+  stroke-dasharray: 2px; }
+
+.ct-grid-background {
+  fill: none; }
+
+.ct-point {
+  stroke-width: 10px;
+  stroke-linecap: round; }
+
+.ct-line {
+  fill: none;
+  stroke-width: 4px; }
+
+.ct-area {
+  stroke: none;
+  fill-opacity: 0.1; }
+
+.ct-bar {
+  fill: none;
+  stroke-width: 10px; }
+
+.ct-slice-donut {
+  fill: none;
+  stroke-width: 60px; }
+
+.ct-series-a .ct-point, .ct-series-a .ct-line, .ct-series-a .ct-bar, .ct-series-a .ct-slice-donut {
+  stroke: #d70206; }
+
+.ct-series-a .ct-slice-pie, .ct-series-a .ct-slice-donut-solid, .ct-series-a .ct-area {
+  fill: #d70206; }
+
+.ct-series-b .ct-point, .ct-series-b .ct-line, .ct-series-b .ct-bar, .ct-series-b .ct-slice-donut {
+  stroke: #f05b4f; }
+
+.ct-series-b .ct-slice-pie, .ct-series-b .ct-slice-donut-solid, .ct-series-b .ct-area {
+  fill: #f05b4f; }
+
+.ct-series-c .ct-point, .ct-series-c .ct-line, .ct-series-c .ct-bar, .ct-series-c .ct-slice-donut {
+  stroke: #f4c63d; }
+
+.ct-series-c .ct-slice-pie, .ct-series-c .ct-slice-donut-solid, .ct-series-c .ct-area {
+  fill: #f4c63d; }
+
+.ct-series-d .ct-point, .ct-series-d .ct-line, .ct-series-d .ct-bar, .ct-series-d .ct-slice-donut {
+  stroke: #d17905; }
+
+.ct-series-d .ct-slice-pie, .ct-series-d .ct-slice-donut-solid, .ct-series-d .ct-area {
+  fill: #d17905; }
+
+.ct-series-e .ct-point, .ct-series-e .ct-line, .ct-series-e .ct-bar, .ct-series-e .ct-slice-donut {
+  stroke: #453d3f; }
+
+.ct-series-e .ct-slice-pie, .ct-series-e .ct-slice-donut-solid, .ct-series-e .ct-area {
+  fill: #453d3f; }
+
+.ct-series-f .ct-point, .ct-series-f .ct-line, .ct-series-f .ct-bar, .ct-series-f .ct-slice-donut {
+  stroke: #59922b; }
+
+.ct-series-f .ct-slice-pie, .ct-series-f .ct-slice-donut-solid, .ct-series-f .ct-area {
+  fill: #59922b; }
+
+.ct-series-g .ct-point, .ct-series-g .ct-line, .ct-series-g .ct-bar, .ct-series-g .ct-slice-donut {
+  stroke: #0544d3; }
+
+.ct-series-g .ct-slice-pie, .ct-series-g .ct-slice-donut-solid, .ct-series-g .ct-area {
+  fill: #0544d3; }
+
+.ct-series-h .ct-point, .ct-series-h .ct-line, .ct-series-h .ct-bar, .ct-series-h .ct-slice-donut {
+  stroke: #6b0392; }
+
+.ct-series-h .ct-slice-pie, .ct-series-h .ct-slice-donut-solid, .ct-series-h .ct-area {
+  fill: #6b0392; }
+
+.ct-series-i .ct-point, .ct-series-i .ct-line, .ct-series-i .ct-bar, .ct-series-i .ct-slice-donut {
+  stroke: #f05b4f; }
+
+.ct-series-i .ct-slice-pie, .ct-series-i .ct-slice-donut-solid, .ct-series-i .ct-area {
+  fill: #f05b4f; }
+
+.ct-series-j .ct-point, .ct-series-j .ct-line, .ct-series-j .ct-bar, .ct-series-j .ct-slice-donut {
+  stroke: #dda458; }
+
+.ct-series-j .ct-slice-pie, .ct-series-j .ct-slice-donut-solid, .ct-series-j .ct-area {
+  fill: #dda458; }
+
+.ct-series-k .ct-point, .ct-series-k .ct-line, .ct-series-k .ct-bar, .ct-series-k .ct-slice-donut {
+  stroke: #eacf7d; }
+
+.ct-series-k .ct-slice-pie, .ct-series-k .ct-slice-donut-solid, .ct-series-k .ct-area {
+  fill: #eacf7d; }
+
+.ct-series-l .ct-point, .ct-series-l .ct-line, .ct-series-l .ct-bar, .ct-series-l .ct-slice-donut {
+  stroke: #86797d; }
+
+.ct-series-l .ct-slice-pie, .ct-series-l .ct-slice-donut-solid, .ct-series-l .ct-area {
+  fill: #86797d; }
+
+.ct-series-m .ct-point, .ct-series-m .ct-line, .ct-series-m .ct-bar, .ct-series-m .ct-slice-donut {
+  stroke: #b2c326; }
+
+.ct-series-m .ct-slice-pie, .ct-series-m .ct-slice-donut-solid, .ct-series-m .ct-area {
+  fill: #b2c326; }
+
+.ct-series-n .ct-point, .ct-series-n .ct-line, .ct-series-n .ct-bar, .ct-series-n .ct-slice-donut {
+  stroke: #6188e2; }
+
+.ct-series-n .ct-slice-pie, .ct-series-n .ct-slice-donut-solid, .ct-series-n .ct-area {
+  fill: #6188e2; }
+
+.ct-series-o .ct-point, .ct-series-o .ct-line, .ct-series-o .ct-bar, .ct-series-o .ct-slice-donut {
+  stroke: #a748ca; }
+
+.ct-series-o .ct-slice-pie, .ct-series-o .ct-slice-donut-solid, .ct-series-o .ct-area {
+  fill: #a748ca; }
+
+.ct-square {
+  display: block;
+  position: relative;
+  width: 100%; }
+  .ct-square:before {
+    display: block;
+    float: left;
+    content: "";
+    width: 0;
+    height: 0;
+    padding-bottom: 100%; }
+  .ct-square:after {
+    content: "";
+    display: table;
+    clear: both; }
+  .ct-square > svg {
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0; }
+
+.ct-minor-second {
+  display: block;
+  position: relative;
+  width: 100%; }
+  .ct-minor-second:before {
+    display: block;
+    float: left;
+    content: "";
+    width: 0;
+    height: 0;
+    padding-bottom: 93.75%; }
+  .ct-minor-second:after {
+    content: "";
+    display: table;
+    clear: both; }
+  .ct-minor-second > svg {
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0; }
+
+.ct-major-second {
+  display: block;
+  position: relative;
+  width: 100%; }
+  .ct-major-second:before {
+    display: block;
+    float: left;
+    content: "";
+    width: 0;
+    height: 0;
+    padding-bottom: 88.8888888889%; }
+  .ct-major-second:after {
+    content: "";
+    display: table;
+    clear: both; }
+  .ct-major-second > svg {
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0; }
+
+.ct-minor-third {
+  display: block;
+  position: relative;
+  width: 100%; }
+  .ct-minor-third:before {
+    display: block;
+    float: left;
+    content: "";
+    width: 0;
+    height: 0;
+    padding-bottom: 83.3333333333%; }
+  .ct-minor-third:after {
+    content: "";
+    display: table;
+    clear: both; }
+  .ct-minor-third > svg {
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0; }
+
+.ct-major-third {
+  display: block;
+  position: relative;
+  width: 100%; }
+  .ct-major-third:before {
+    display: block;
+    float: left;
+    content: "";
+    width: 0;
+    height: 0;
+    padding-bottom: 80%; }
+  .ct-major-third:after {
+    content: "";
+    display: table;
+    clear: both; }
+  .ct-major-third > svg {
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0; }
+
+.ct-perfect-fourth {
+  display: block;
+  position: relative;
+  width: 100%; }
+  .ct-perfect-fourth:before {
+    display: block;
+    float: left;
+    content: "";
+    width: 0;
+    height: 0;
+    padding-bottom: 75%; }
+  .ct-perfect-fourth:after {
+    content: "";
+    display: table;
+    clear: both; }
+  .ct-perfect-fourth > svg {
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0; }
+
+.ct-perfect-fifth {
+  display: block;
+  position: relative;
+  width: 100%; }
+  .ct-perfect-fifth:before {
+    display: block;
+    float: left;
+    content: "";
+    width: 0;
+    height: 0;
+    padding-bottom: 66.6666666667%; }
+  .ct-perfect-fifth:after {
+    content: "";
+    display: table;
+    clear: both; }
+  .ct-perfect-fifth > svg {
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0; }
+
+.ct-minor-sixth {
+  display: block;
+  position: relative;
+  width: 100%; }
+  .ct-minor-sixth:before {
+    display: block;
+    float: left;
+    content: "";
+    width: 0;
+    height: 0;
+    padding-bottom: 62.5%; }
+  .ct-minor-sixth:after {
+    content: "";
+    display: table;
+    clear: both; }
+  .ct-minor-sixth > svg {
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0; }
+
+.ct-golden-section {
+  display: block;
+  position: relative;
+  width: 100%; }
+  .ct-golden-section:before {
+    display: block;
+    float: left;
+    content: "";
+    width: 0;
+    height: 0;
+    padding-bottom: 61.804697157%; }
+  .ct-golden-section:after {
+    content: "";
+    display: table;
+    clear: both; }
+  .ct-golden-section > svg {
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0; }
+
+.ct-major-sixth {
+  display: block;
+  position: relative;
+  width: 100%; }
+  .ct-major-sixth:before {
+    display: block;
+    float: left;
+    content: "";
+    width: 0;
+    height: 0;
+    padding-bottom: 60%; }
+  .ct-major-sixth:after {
+    content: "";
+    display: table;
+    clear: both; }
+  .ct-major-sixth > svg {
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0; }
+
+.ct-minor-seventh {
+  display: block;
+  position: relative;
+  width: 100%; }
+  .ct-minor-seventh:before {
+    display: block;
+    float: left;
+    content: "";
+    width: 0;
+    height: 0;
+    padding-bottom: 56.25%; }
+  .ct-minor-seventh:after {
+    content: "";
+    display: table;
+    clear: both; }
+  .ct-minor-seventh > svg {
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0; }
+
+.ct-major-seventh {
+  display: block;
+  position: relative;
+  width: 100%; }
+  .ct-major-seventh:before {
+    display: block;
+    float: left;
+    content: "";
+    width: 0;
+    height: 0;
+    padding-bottom: 53.3333333333%; }
+  .ct-major-seventh:after {
+    content: "";
+    display: table;
+    clear: both; }
+  .ct-major-seventh > svg {
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0; }
+
+.ct-octave {
+  display: block;
+  position: relative;
+  width: 100%; }
+  .ct-octave:before {
+    display: block;
+    float: left;
+    content: "";
+    width: 0;
+    height: 0;
+    padding-bottom: 50%; }
+  .ct-octave:after {
+    content: "";
+    display: table;
+    clear: both; }
+  .ct-octave > svg {
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0; }
+
+.ct-major-tenth {
+  display: block;
+  position: relative;
+  width: 100%; }
+  .ct-major-tenth:before {
+    display: block;
+    float: left;
+    content: "";
+    width: 0;
+    height: 0;
+    padding-bottom: 40%; }
+  .ct-major-tenth:after {
+    content: "";
+    display: table;
+    clear: both; }
+  .ct-major-tenth > svg {
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0; }
+
+.ct-major-eleventh {
+  display: block;
+  position: relative;
+  width: 100%; }
+  .ct-major-eleventh:before {
+    display: block;
+    float: left;
+    content: "";
+    width: 0;
+    height: 0;
+    padding-bottom: 37.5%; }
+  .ct-major-eleventh:after {
+    content: "";
+    display: table;
+    clear: both; }
+  .ct-major-eleventh > svg {
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0; }
+
+.ct-major-twelfth {
+  display: block;
+  position: relative;
+  width: 100%; }
+  .ct-major-twelfth:before {
+    display: block;
+    float: left;
+    content: "";
+    width: 0;
+    height: 0;
+    padding-bottom: 33.3333333333%; }
+  .ct-major-twelfth:after {
+    content: "";
+    display: table;
+    clear: both; }
+  .ct-major-twelfth > svg {
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0; }
+
+.ct-double-octave {
+  display: block;
+  position: relative;
+  width: 100%; }
+  .ct-double-octave:before {
+    display: block;
+    float: left;
+    content: "";
+    width: 0;
+    height: 0;
+    padding-bottom: 25%; }
+  .ct-double-octave:after {
+    content: "";
+    display: table;
+    clear: both; }
+  .ct-double-octave > svg {
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0; }
+
+/*# sourceMappingURL=chartist.css.map */

File diff suppressed because it is too large
+ 10 - 0
src/api/static/chartist/chartist.css.map


File diff suppressed because it is too large
+ 4488 - 0
src/api/static/chartist/chartist.js


File diff suppressed because it is too large
+ 1 - 0
src/api/static/chartist/chartist.min.css


File diff suppressed because it is too large
+ 10 - 0
src/api/static/chartist/chartist.min.js


File diff suppressed because it is too large
+ 1 - 0
src/api/static/chartist/chartist.min.js.map


+ 241 - 0
src/api/static/chartist/scss/chartist.scss

@@ -0,0 +1,241 @@
+@import "settings/chartist-settings";
+
+@mixin ct-responsive-svg-container($width: 100%, $ratio: $ct-container-ratio) {
+  display: block;
+  position: relative;
+  width: $width;
+
+  &:before {
+    display: block;
+    float: left;
+    content: "";
+    width: 0;
+    height: 0;
+    padding-bottom: $ratio * 100%;
+  }
+
+  &:after {
+    content: "";
+    display: table;
+    clear: both;
+  }
+
+  > svg {
+    display: block;
+    position: absolute;
+    top: 0;
+    left: 0;
+  }
+}
+
+@mixin ct-align-justify($ct-text-align: $ct-text-align, $ct-text-justify: $ct-text-justify) {
+  -webkit-box-align: $ct-text-align;
+  -webkit-align-items: $ct-text-align;
+  -ms-flex-align: $ct-text-align;
+  align-items: $ct-text-align;
+  -webkit-box-pack: $ct-text-justify;
+  -webkit-justify-content: $ct-text-justify;
+  -ms-flex-pack: $ct-text-justify;
+  justify-content: $ct-text-justify;
+  // Fallback to text-align for non-flex browsers
+  @if($ct-text-justify == 'flex-start') {
+    text-align: left;
+  } @else if ($ct-text-justify == 'flex-end') {
+    text-align: right;
+  } @else {
+    text-align: center;
+  }
+}
+
+@mixin ct-flex() {
+  // Fallback to block
+  display: block;
+  display: -webkit-box;
+  display: -moz-box;
+  display: -ms-flexbox;
+  display: -webkit-flex;
+  display: flex;
+}
+
+@mixin ct-chart-label($ct-text-color: $ct-text-color, $ct-text-size: $ct-text-size, $ct-text-line-height: $ct-text-line-height) {
+  fill: $ct-text-color;
+  color: $ct-text-color;
+  font-size: $ct-text-size;
+  line-height: $ct-text-line-height;
+}
+
+@mixin ct-chart-grid($ct-grid-color: $ct-grid-color, $ct-grid-width: $ct-grid-width, $ct-grid-dasharray: $ct-grid-dasharray) {
+  stroke: $ct-grid-color;
+  stroke-width: $ct-grid-width;
+
+  @if ($ct-grid-dasharray) {
+    stroke-dasharray: $ct-grid-dasharray;
+  }
+}
+
+@mixin ct-chart-point($ct-point-size: $ct-point-size, $ct-point-shape: $ct-point-shape) {
+  stroke-width: $ct-point-size;
+  stroke-linecap: $ct-point-shape;
+}
+
+@mixin ct-chart-line($ct-line-width: $ct-line-width, $ct-line-dasharray: $ct-line-dasharray) {
+  fill: none;
+  stroke-width: $ct-line-width;
+
+  @if ($ct-line-dasharray) {
+    stroke-dasharray: $ct-line-dasharray;
+  }
+}
+
+@mixin ct-chart-area($ct-area-opacity: $ct-area-opacity) {
+  stroke: none;
+  fill-opacity: $ct-area-opacity;
+}
+
+@mixin ct-chart-bar($ct-bar-width: $ct-bar-width) {
+  fill: none;
+  stroke-width: $ct-bar-width;
+}
+
+@mixin ct-chart-donut($ct-donut-width: $ct-donut-width) {
+  fill: none;
+  stroke-width: $ct-donut-width;
+}
+
+@mixin ct-chart-series-color($color) {
+  .#{$ct-class-point}, .#{$ct-class-line}, .#{$ct-class-bar}, .#{$ct-class-slice-donut} {
+    stroke: $color;
+  }
+
+  .#{$ct-class-slice-pie}, .#{$ct-class-slice-donut-solid}, .#{$ct-class-area} {
+    fill: $color;
+  }
+}
+
+@mixin ct-chart($ct-container-ratio: $ct-container-ratio, $ct-text-color: $ct-text-color, $ct-text-size: $ct-text-size, $ct-grid-color: $ct-grid-color, $ct-grid-width: $ct-grid-width, $ct-grid-dasharray: $ct-grid-dasharray, $ct-point-size: $ct-point-size, $ct-point-shape: $ct-point-shape, $ct-line-width: $ct-line-width, $ct-bar-width: $ct-bar-width, $ct-donut-width: $ct-donut-width, $ct-series-names: $ct-series-names, $ct-series-colors: $ct-series-colors) {
+
+  .#{$ct-class-label} {
+    @include ct-chart-label($ct-text-color, $ct-text-size);
+  }
+
+  .#{$ct-class-chart-line} .#{$ct-class-label},
+  .#{$ct-class-chart-bar} .#{$ct-class-label} {
+    @include ct-flex();
+  }
+
+  .#{$ct-class-chart-pie} .#{$ct-class-label},
+  .#{$ct-class-chart-donut} .#{$ct-class-label} {
+    dominant-baseline: central;
+  }
+
+  .#{$ct-class-label}.#{$ct-class-horizontal}.#{$ct-class-start} {
+    @include ct-align-justify(flex-end, flex-start);
+    // Fallback for browsers that don't support foreignObjects
+    text-anchor: start;
+  }
+
+  .#{$ct-class-label}.#{$ct-class-horizontal}.#{$ct-class-end} {
+    @include ct-align-justify(flex-start, flex-start);
+    // Fallback for browsers that don't support foreignObjects
+    text-anchor: start;
+  }
+
+  .#{$ct-class-label}.#{$ct-class-vertical}.#{$ct-class-start} {
+    @include ct-align-justify(flex-end, flex-end);
+    // Fallback for browsers that don't support foreignObjects
+    text-anchor: end;
+  }
+
+  .#{$ct-class-label}.#{$ct-class-vertical}.#{$ct-class-end} {
+    @include ct-align-justify(flex-end, flex-start);
+    // Fallback for browsers that don't support foreignObjects
+    text-anchor: start;
+  }
+
+  .#{$ct-class-chart-bar} .#{$ct-class-label}.#{$ct-class-horizontal}.#{$ct-class-start} {
+    @include ct-align-justify(flex-end, center);
+    // Fallback for browsers that don't support foreignObjects
+    text-anchor: start;
+  }
+
+  .#{$ct-class-chart-bar} .#{$ct-class-label}.#{$ct-class-horizontal}.#{$ct-class-end} {
+    @include ct-align-justify(flex-start, center);
+    // Fallback for browsers that don't support foreignObjects
+    text-anchor: start;
+  }
+
+  .#{$ct-class-chart-bar}.#{$ct-class-horizontal-bars} .#{$ct-class-label}.#{$ct-class-horizontal}.#{$ct-class-start} {
+    @include ct-align-justify(flex-end, flex-start);
+    // Fallback for browsers that don't support foreignObjects
+    text-anchor: start;
+  }
+
+  .#{$ct-class-chart-bar}.#{$ct-class-horizontal-bars} .#{$ct-class-label}.#{$ct-class-horizontal}.#{$ct-class-end} {
+    @include ct-align-justify(flex-start, flex-start);
+    // Fallback for browsers that don't support foreignObjects
+    text-anchor: start;
+  }
+
+  .#{$ct-class-chart-bar}.#{$ct-class-horizontal-bars} .#{$ct-class-label}.#{$ct-class-vertical}.#{$ct-class-start} {
+    //@include ct-chart-label($ct-text-color, $ct-text-size, center, $ct-vertical-text-justify);
+    @include ct-align-justify(center, flex-end);
+    // Fallback for browsers that don't support foreignObjects
+    text-anchor: end;
+  }
+
+  .#{$ct-class-chart-bar}.#{$ct-class-horizontal-bars} .#{$ct-class-label}.#{$ct-class-vertical}.#{$ct-class-end} {
+    @include ct-align-justify(center, flex-start);
+    // Fallback for browsers that don't support foreignObjects
+    text-anchor: end;
+  }
+
+  .#{$ct-class-grid} {
+    @include ct-chart-grid($ct-grid-color, $ct-grid-width, $ct-grid-dasharray);
+  }
+
+  .#{$ct-class-grid-background} {
+    fill: $ct-grid-background-fill;
+  }
+
+  .#{$ct-class-point} {
+    @include ct-chart-point($ct-point-size, $ct-point-shape);
+  }
+
+  .#{$ct-class-line} {
+    @include ct-chart-line($ct-line-width);
+  }
+
+  .#{$ct-class-area} {
+    @include ct-chart-area();
+  }
+
+  .#{$ct-class-bar} {
+    @include ct-chart-bar($ct-bar-width);
+  }
+
+  .#{$ct-class-slice-donut} {
+    @include ct-chart-donut($ct-donut-width);
+  }
+
+  @if $ct-include-colored-series {
+    @for $i from 0 to length($ct-series-names) {
+      .#{$ct-class-series}-#{nth($ct-series-names, $i + 1)} {
+        $color: nth($ct-series-colors, $i + 1);
+
+        @include ct-chart-series-color($color);
+      }
+    }
+  }
+}
+
+@if $ct-include-classes {
+  @include ct-chart();
+
+  @if $ct-include-alternative-responsive-containers {
+    @for $i from 0 to length($ct-scales-names) {
+      .#{nth($ct-scales-names, $i + 1)} {
+        @include ct-responsive-svg-container($ratio: nth($ct-scales, $i + 1));
+      }
+    }
+  }
+}

+ 88 - 0
src/api/static/chartist/scss/settings/_chartist-settings.scss

@@ -0,0 +1,88 @@
+// Scales for responsive SVG containers
+$ct-scales: ((1), (15/16), (8/9), (5/6), (4/5), (3/4), (2/3), (5/8), (1/1.618), (3/5), (9/16), (8/15), (1/2), (2/5), (3/8), (1/3), (1/4)) !default;
+$ct-scales-names: (ct-square, ct-minor-second, ct-major-second, ct-minor-third, ct-major-third, ct-perfect-fourth, ct-perfect-fifth, ct-minor-sixth, ct-golden-section, ct-major-sixth, ct-minor-seventh, ct-major-seventh, ct-octave, ct-major-tenth, ct-major-eleventh, ct-major-twelfth, ct-double-octave) !default;
+
+// Class names to be used when generating CSS
+$ct-class-chart: ct-chart !default;
+$ct-class-chart-line: ct-chart-line !default;
+$ct-class-chart-bar: ct-chart-bar !default;
+$ct-class-horizontal-bars: ct-horizontal-bars !default;
+$ct-class-chart-pie: ct-chart-pie !default;
+$ct-class-chart-donut: ct-chart-donut !default;
+$ct-class-label: ct-label !default;
+$ct-class-series: ct-series !default;
+$ct-class-line: ct-line !default;
+$ct-class-point: ct-point !default;
+$ct-class-area: ct-area !default;
+$ct-class-bar: ct-bar !default;
+$ct-class-slice-pie: ct-slice-pie !default;
+$ct-class-slice-donut: ct-slice-donut !default;
+$ct-class-slice-donut-solid: ct-slice-donut-solid !default;
+$ct-class-grid: ct-grid !default;
+$ct-class-grid-background: ct-grid-background !default;
+$ct-class-vertical: ct-vertical !default;
+$ct-class-horizontal: ct-horizontal !default;
+$ct-class-start: ct-start !default;
+$ct-class-end: ct-end !default;
+
+// Container ratio
+$ct-container-ratio: (1/1.618) !default;
+
+// Text styles for labels
+$ct-text-color: rgba(0, 0, 0, 0.4) !default;
+$ct-text-size: 0.75rem !default;
+$ct-text-align: flex-start !default;
+$ct-text-justify: flex-start !default;
+$ct-text-line-height: 1;
+
+// Grid styles
+$ct-grid-color: rgba(0, 0, 0, 0.2) !default;
+$ct-grid-dasharray: 2px !default;
+$ct-grid-width: 1px !default;
+$ct-grid-background-fill: none !default;
+
+// Line chart properties
+$ct-line-width: 4px !default;
+$ct-line-dasharray: false !default;
+$ct-point-size: 10px !default;
+// Line chart point, can be either round or square
+$ct-point-shape: round !default;
+// Area fill transparency between 0 and 1
+$ct-area-opacity: 0.1 !default;
+
+// Bar chart bar width
+$ct-bar-width: 10px !default;
+
+// Donut width (If donut width is to big it can cause issues where the shape gets distorted)
+$ct-donut-width: 60px !default;
+
+// If set to true it will include the default classes and generate CSS output. If you're planning to use the mixins you
+// should set this property to false
+$ct-include-classes: true !default;
+
+// If this is set to true the CSS will contain colored series. You can extend or change the color with the
+// properties below
+$ct-include-colored-series: $ct-include-classes !default;
+
+// If set to true this will include all responsive container variations using the scales defined at the top of the script
+$ct-include-alternative-responsive-containers: $ct-include-classes !default;
+
+// Series names and colors. This can be extended or customized as desired. Just add more series and colors.
+$ct-series-names: (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) !default;
+$ct-series-colors: (
+  #d70206,
+  #f05b4f,
+  #f4c63d,
+  #d17905,
+  #453d3f,
+  #59922b,
+  #0544d3,
+  #6b0392,
+  #f05b4f,
+  #dda458,
+  #eacf7d,
+  #86797d,
+  #b2c326,
+  #6188e2,
+  #a748ca
+) !default;

File diff suppressed because it is too large
+ 6 - 0
src/api/static/css/animate.min.css


File diff suppressed because it is too large
+ 5 - 0
src/api/static/css/bootstrap.min.css


File diff suppressed because it is too large
+ 2752 - 0
src/api/static/css/dashboard.css


+ 0 - 146
src/api/static/css/default.css

@@ -1,146 +0,0 @@
-body, .default {
-	font-size:14px;
-	font-family:sans-serif;
-	font-weight:lighter;
-	padding:10px;
-	
-}
-.jsgrid-grid-header{
-	background: #f9f9f9;
-	background-color: rgb(249, 249, 249);
-	background-image: none;
-	background-repeat: repeat;
-	background-attachment: scroll;
-	background-clip: border-box;
-	background-origin: padding-box;
-	background-position-x: 0%;
-	background-position-y: 0%;
-	background-size: auto auto;
-	max-height: 48px;
-}
-.jsgrid-grid-body {
-	overflow-x:hidden;
-	overflow-y:scroll;
-	-webkit-overflow-scrolling:touch
-}
-
-
-.small {
-	font-family:sans-serif;
-	font-size:12px;
-	font-weight:lighter;
-}
-.bold {font-weight:bold}
-.left {float:left}
-.right{float:right}
-.caption {
-	font-size:22px;
-	margin:2px;
-	padding:2px;
-	height:30px;
-	font-family:sans-serif;
-	font-weight:lighter;
-	
-}
-.button {
-	padding:8px;
-	margin:2px;
-	border-radius:4px;
-	-moz-border-radius:4px;
-	-webkit-border-radius:4px;
-	cursor:pointer;
-}
-.button:hover{
-	background-color:#4682B4 ;
-	color:#ffffff;
-	
-}
-.no-border{ border:1px solid transparent}
-.border { border:1px solid #CAD5E0}
-.border-bottom{	border-bottom:1px solid #CAD5E0;}
-.border-right { border-right:1px solid #CAD5E0;}
-.border-left { border-left:1px solid #CAD5E0;}
-.border-top { border-top:1px solid #CAD5E0;}
-.grid {
-	font-family:sans-serif;
-	font-weight:lighter;
-	
-	margin:4px;
-	padding:4px;
-}
-.grid-half {
-	height:195px;
-	margin:4px;
-	width:98%;
-	padding:4px;
-}
-.grid .fa-check {color:green}
-.grid .fa-times {color:maroon; }
-
-.menu {
-	margin:4px;
-	padding:4px;
-}
-.menu .fa-chevron-right {color:transparent; margin:4px; }
-.menu .menu-item { border:1px solid transparent; cursor:pointer; padding-bottom:4px; margin:2px;}
-.menu .menu-item:hover {
-	border-bottom-color:#4682B4;
-}
-.menu .menu-item:hover .fa-chevron-right { color:#4582b4}
-input[type=text]{
-	padding:4px;
-	margin:4px;
-	outline:0px;
-	border:1px solid transparent;
-	background-color:#f3f3f3;
-	font-size:14px;
-	font-weight:lighter;
-	font-family:sans-serif;
-}
-input[type=text]:focus{
-	border-left-color:#4682B4;
-}
-
-.padding-2x{padding:4px;}
-.margin-2x {margin:4px;}
-.info {
-	
-	margin:4px;
-	width:43%;
-	
-	
-}
-.height-quarter{height:24%;}
-.height-half{height:47%}
-.width-quarter {width:24%}
-.width-half {width:47%; }
-.width-75 {width:72%}
-
-.bad { color:maroon}
-.good{ color:green}
-.fa-warning, .warning{color:orange}
-.number {font-size:42px; font-weight:lighter; padding:2px; margin:2px;}
-
-.action {cursor:pointer; padding:2px; margin:2px; border:1px solid transparent}
-.action:hover { border-bottom-color:#4682B4}
-.shadow{box-shadow: 7px 7px 5px #888888;}
-.gradient {
-  background-image:
-    linear-gradient(
-      #4682b4, #ffffff,#ffffff
-    );
-}
-.simple-gradient {
-background-image: -ms-linear-gradient(top, #CAD5E0 0%, #F3F3F3 40%);
-
-background-image: -moz-linear-gradient(top, #CAD5E0 0%, #F3F3F3 40%);
-
-background-image: -o-linear-gradient(top, #CAD5E0 0%, #F3F3F3 40%);
-
-background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #CAD5E0), color-stop(40, #F3F3F3));
-
-background-image: -webkit-linear-gradient(top, #CAD5E0 0%, #F3F3F3 40%);
-
-background-image: linear-gradient(to bottom, #CAD5E0 0%, #F3F3F3 40%);
-	
-}

+ 1 - 1
src/api/static/css/fa/scss/_core.scss

@@ -5,7 +5,7 @@
   display: inline-block;
   display: inline-block;
   font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration
   font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration
   font-size: inherit; // can't have font-size inherit on line above, so need to override
   font-size: inherit; // can't have font-size inherit on line above, so need to override
-  text-rendering: auto; // optimizelegibility throws things off #1094
+  text-rendering: auto; // optimize legibility throws things off #1094
   -webkit-font-smoothing: antialiased;
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
   -moz-osx-font-smoothing: grayscale;
 
 

File diff suppressed because it is too large
+ 1081 - 0
src/api/static/css/themify-icons.css


BIN
src/api/static/fonts/themify.eot


File diff suppressed because it is too large
+ 362 - 0
src/api/static/fonts/themify.svg


BIN
src/api/static/fonts/themify.ttf


BIN
src/api/static/fonts/themify.woff


File diff suppressed because it is too large
+ 243 - 0
src/api/static/img/uml-design


+ 0 - 12
src/api/static/index.html

@@ -1,12 +0,0 @@
-<link type="text/css" rel="stylesheet" href="{{ context }}/js/jsgrid/jsgrid.min.css" />
-<link type="text/css" rel="stylesheet" href="{{ context }}/js/jsgrid/jsgrid-theme.min.css" />
-<script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
-<script src="{{ context }}/static/js/jsgrid.js"></script>
-<script src="{{ context }}/static/js/jquery/jquery.min.js"></script>
-
-<title></title>
-<body>
-<div class="caption">
-<div class="left">Process Monitoring</div>
-</div>
-</body>

BIN
src/api/static/js/.dashboard.js.un~


+ 248 - 0
src/api/static/js/bootstrap-checkbox-radio.js

@@ -0,0 +1,248 @@
+!function ($) {
+
+ /* CHECKBOX PUBLIC CLASS DEFINITION
+  * ============================== */
+
+  var Checkbox = function (element, options) {
+    this.init(element, options);
+  }
+
+  Checkbox.prototype = {
+
+    constructor: Checkbox
+
+  , init: function (element, options) {
+    var $el = this.$element = $(element)
+
+    this.options = $.extend({}, $.fn.checkbox.defaults, options);
+    $el.before(this.options.template);
+    this.setState();
+  }
+
+  , setState: function () {
+      var $el = this.$element
+        , $parent = $el.closest('.checkbox');
+
+        $el.prop('disabled') && $parent.addClass('disabled');
+        $el.prop('checked') && $parent.addClass('checked');
+    }
+
+  , toggle: function () {
+      var ch = 'checked'
+        , $el = this.$element
+        , $parent = $el.closest('.checkbox')
+        , checked = $el.prop(ch)
+        , e = $.Event('toggle')
+
+      if ($el.prop('disabled') == false) {
+        $parent.toggleClass(ch) && checked ? $el.removeAttr(ch) : $el.prop(ch, ch);
+        $el.trigger(e).trigger('change');
+      }
+    }
+
+  , setCheck: function (option) {
+      var d = 'disabled'
+        , ch = 'checked'
+        , $el = this.$element
+        , $parent = $el.closest('.checkbox')
+        , checkAction = option == 'check' ? true : false
+        , e = $.Event(option)
+
+      $parent[checkAction ? 'addClass' : 'removeClass' ](ch) && checkAction ? $el.prop(ch, ch) : $el.removeAttr(ch);
+      $el.trigger(e).trigger('change');
+    }
+
+  }
+
+
+ /* CHECKBOX PLUGIN DEFINITION
+  * ======================== */
+
+  var old = $.fn.checkbox
+
+  $.fn.checkbox = function (option) {
+    return this.each(function () {
+      var $this = $(this)
+        , data = $this.data('checkbox')
+        , options = $.extend({}, $.fn.checkbox.defaults, $this.data(), typeof option == 'object' && option);
+      if (!data) $this.data('checkbox', (data = new Checkbox(this, options)));
+      if (option == 'toggle') data.toggle()
+      if (option == 'check' || option == 'uncheck') data.setCheck(option)
+      else if (option) data.setState();
+    });
+  }
+
+  $.fn.checkbox.defaults = {
+    template: '<span class="icons"><span class="first-icon fa fa-square fa-base"></span><span class="second-icon fa fa-check-square fa-base"></span></span>'
+  }
+
+
+ /* CHECKBOX NO CONFLICT
+  * ================== */
+
+  $.fn.checkbox.noConflict = function () {
+    $.fn.checkbox = old;
+    return this;
+  }
+
+
+ /* CHECKBOX DATA-API
+  * =============== */
+
+  $(document).on('click.checkbox.data-api', '[data-toggle^=checkbox], .checkbox', function (e) {
+    var $checkbox = $(e.target);
+    if (e.target.tagName != "A") {
+      e && e.preventDefault() && e.stopPropagation();
+      if (!$checkbox.hasClass('checkbox')) $checkbox = $checkbox.closest('.checkbox');
+      $checkbox.find(':checkbox').checkbox('toggle');
+    }
+  });
+
+  $(function () {
+    $('input[type="checkbox"]').each(function () {
+      var $checkbox = $(this);
+      $checkbox.checkbox();
+    });
+  });
+
+}(window.jQuery);
+
+/* =============================================================
+ * flatui-radio v0.0.3
+ * ============================================================ */
+
+!function ($) {
+
+ /* RADIO PUBLIC CLASS DEFINITION
+  * ============================== */
+
+  var Radio = function (element, options) {
+    this.init(element, options);
+  }
+
+  Radio.prototype = {
+
+    constructor: Radio
+
+  , init: function (element, options) {
+      var $el = this.$element = $(element)
+
+      this.options = $.extend({}, $.fn.radio.defaults, options);
+      $el.before(this.options.template);
+      this.setState();
+    }
+
+  , setState: function () {
+      var $el = this.$element
+        , $parent = $el.closest('.radio');
+
+        $el.prop('disabled') && $parent.addClass('disabled');
+        $el.prop('checked') && $parent.addClass('checked');
+    }
+
+  , toggle: function () {
+      var d = 'disabled'
+        , ch = 'checked'
+        , $el = this.$element
+        , checked = $el.prop(ch)
+        , $parent = $el.closest('.radio')
+        , $parentWrap = $el.closest('form').length ? $el.closest('form') : $el.closest('body')
+        , $elemGroup = $parentWrap.find(':radio[name="' + $el.attr('name') + '"]')
+        , e = $.Event('toggle')
+
+        if ($el.prop(d) == false) {
+            $elemGroup.not($el).each(function () {
+              var $el = $(this)
+                , $parent = $(this).closest('.radio');
+
+                if ($el.prop(d) == false) {
+                  $parent.removeClass(ch) && $el.removeAttr(ch).trigger('change');
+                }
+            });
+
+            if (checked == false) $parent.addClass(ch) && $el.prop(ch, true);
+            $el.trigger(e);
+
+            if (checked !== $el.prop(ch)) {
+                $el.trigger('change');
+            }
+        }
+    }
+
+  , setCheck: function (option) {
+      var ch = 'checked'
+        , $el = this.$element
+        , $parent = $el.closest('.radio')
+        , checkAction = option == 'check' ? true : false
+        , checked = $el.prop(ch)
+        , $parentWrap = $el.closest('form').length ? $el.closest('form') : $el.closest('body')
+        , $elemGroup = $parentWrap.find(':radio[name="' + $el['attr']('name') + '"]')
+        , e = $.Event(option)
+
+      $elemGroup.not($el).each(function () {
+        var $el = $(this)
+          , $parent = $(this).closest('.radio');
+
+          $parent.removeClass(ch) && $el.removeAttr(ch);
+      });
+
+      $parent[checkAction ? 'addClass' : 'removeClass'](ch) && checkAction ? $el.prop(ch, ch) : $el.removeAttr(ch);
+      $el.trigger(e);
+
+      if (checked !== $el.prop(ch)) {
+        $el.trigger('change');
+      }
+    }
+
+  }
+
+
+ /* RADIO PLUGIN DEFINITION
+  * ======================== */
+
+  var old = $.fn.radio
+
+  $.fn.radio = function (option) {
+    return this.each(function () {
+      var $this = $(this)
+        , data = $this.data('radio')
+        , options = $.extend({}, $.fn.radio.defaults, $this.data(), typeof option == 'object' && option);
+      if (!data) $this.data('radio', (data = new Radio(this, options)));
+      if (option == 'toggle') data.toggle()
+      if (option == 'check' || option == 'uncheck') data.setCheck(option)
+      else if (option) data.setState();
+    });
+  }
+
+  $.fn.radio.defaults = {
+    template: '<span class="icons"><span class="first-icon fa fa-circle-o fa-base"></span><span class="second-icon fa fa-dot-circle-o fa-base"></span></span>'
+  }
+
+
+ /* RADIO NO CONFLICT
+  * ================== */
+
+  $.fn.radio.noConflict = function () {
+    $.fn.radio = old;
+    return this;
+  }
+
+
+ /* RADIO DATA-API
+  * =============== */
+
+  $(document).on('click.radio.data-api', '[data-toggle^=radio], .radio', function (e) {
+    var $radio = $(e.target);
+    e && e.preventDefault() && e.stopPropagation();
+    if (!$radio.hasClass('radio')) $radio = $radio.closest('.radio');
+    $radio.find(':radio').radio('toggle');
+  });
+
+  $(function () {
+    $('input[type="radio"]').each(function () {
+      var $radio = $(this);
+      $radio.radio();
+    });
+  });
+
+}(window.jQuery);

File diff suppressed because it is too large
+ 404 - 0
src/api/static/js/bootstrap-notify.js


File diff suppressed because it is too large
+ 7 - 0
src/api/static/js/bootstrap.min.js


File diff suppressed because it is too large
+ 9 - 0
src/api/static/js/chartist.min.js


File diff suppressed because it is too large
+ 216 - 0
src/api/static/js/chartist.min.js.map


File diff suppressed because it is too large
+ 0 - 2
src/api/static/js/colors.js


File diff suppressed because it is too large
+ 180 - 561
src/api/static/js/dashboard.js


+ 140 - 0
src/api/static/js/default.js

@@ -0,0 +1,140 @@
+var fixedTop = false;
+var transparent = true;
+var navbar_initialized = false;
+
+$(document).ready(function(){
+    window_width = $(window).width();
+
+    // Init navigation toggle for small screens
+    if(window_width <= 991){
+        pd.initRightMenu();
+    }
+
+    //  Activate the tooltips
+    $('[rel="tooltip"]').tooltip();
+
+});
+
+// activate collapse right menu when the windows is resized
+$(window).resize(function(){
+    if($(window).width() <= 991){
+        pd.initRightMenu();
+    }
+});
+
+pd = {
+    misc:{
+        navbar_menu_visible: 0
+    },
+    checkScrollForTransparentNavbar: debounce(function() {
+        if($(document).scrollTop() > 381 ) {
+            if(transparent) {
+                transparent = false;
+                $('.navbar-color-on-scroll').removeClass('navbar-transparent');
+                $('.navbar-title').removeClass('hidden');
+            }
+        } else {
+            if( !transparent ) {
+                transparent = true;
+                $('.navbar-color-on-scroll').addClass('navbar-transparent');
+                $('.navbar-title').addClass('hidden');
+            }
+        }
+    }),
+    initRightMenu: function(){
+         if(!navbar_initialized){
+            $off_canvas_sidebar = $('nav').find('.navbar-collapse').first().clone(true);
+
+            $sidebar = $('.sidebar');
+            sidebar_bg_color = $sidebar.data('background-color');
+            sidebar_active_color = $sidebar.data('active-color');
+
+            $logo = $sidebar.find('.logo').first();
+            logo_content = $logo[0].outerHTML;
+
+            ul_content = '';
+
+            // set the bg color and active color from the default sidebar to the off canvas sidebar;
+            $off_canvas_sidebar.attr('data-background-color',sidebar_bg_color);
+            $off_canvas_sidebar.attr('data-active-color',sidebar_active_color);
+
+            $off_canvas_sidebar.addClass('off-canvas-sidebar');
+
+            //add the content from the regular header to the right menu
+            $off_canvas_sidebar.children('ul').each(function(){
+                content_buff = $(this).html();
+                ul_content = ul_content + content_buff;
+            });
+
+            // add the content from the sidebar to the right menu
+            content_buff = $sidebar.find('.nav').html();
+            ul_content = ul_content + '<li class="divider"></li>'+ content_buff;
+
+            ul_content = '<ul class="nav navbar-nav">' + ul_content + '</ul>';
+
+            navbar_content = logo_content + ul_content;
+            navbar_content = '<div class="sidebar-wrapper">' + navbar_content + '</div>';
+
+            $off_canvas_sidebar.html(navbar_content);
+
+            $('body').append($off_canvas_sidebar);
+
+             $toggle = $('.navbar-toggle');
+
+             $off_canvas_sidebar.find('a').removeClass('btn btn-round btn-default');
+             $off_canvas_sidebar.find('button').removeClass('btn-round btn-fill btn-info btn-primary btn-success btn-danger btn-warning btn-neutral');
+             $off_canvas_sidebar.find('button').addClass('btn-simple btn-block');
+
+             $toggle.click(function (){
+                if(pd.misc.navbar_menu_visible == 1) {
+                    $('html').removeClass('nav-open');
+                    pd.misc.navbar_menu_visible = 0;
+                    $('#bodyClick').remove();
+                     setTimeout(function(){
+                        $toggle.removeClass('toggled');
+                     }, 400);
+
+                } else {
+                    setTimeout(function(){
+                        $toggle.addClass('toggled');
+                    }, 430);
+
+                    div = '<div id="bodyClick"></div>';
+                    $(div).appendTo("body").click(function() {
+                        $('html').removeClass('nav-open');
+                        pd.misc.navbar_menu_visible = 0;
+                        $('#bodyClick').remove();
+                         setTimeout(function(){
+                            $toggle.removeClass('toggled');
+                         }, 400);
+                    });
+
+                    $('html').addClass('nav-open');
+                    pd.misc.navbar_menu_visible = 1;
+
+                }
+            });
+            navbar_initialized = true;
+        }
+
+    }
+}
+
+
+// Returns a function, that, as long as it continues to be invoked, will not
+// be triggered. The function will be called after it stops being called for
+// N milliseconds. If `immediate` is passed, trigger the function on the
+// leading edge, instead of the trailing.
+
+function debounce(func, wait, immediate) {
+	var timeout;
+	return function() {
+		var context = this, args = arguments;
+		clearTimeout(timeout);
+		timeout = setTimeout(function() {
+			timeout = null;
+			if (!immediate) func.apply(context, args);
+		}, wait);
+		if (immediate && !timeout) func.apply(context, args);
+	};
+};

File diff suppressed because it is too large
+ 9789 - 0
src/api/static/js/jquery-1.10.2.js


+ 401 - 171
src/api/templates/dashboard.html

@@ -1,172 +1,402 @@
-<meta charset="UTF-8">
-<meta http-equiv="cache-control" content="no-cache">
-<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1">
-<link type="text/css" rel="stylesheet" href="{{ context }}/static/js/jsgrid/jsgrid.min.css" >
-<link type="text/css" rel="stylesheet" href="{{ context }}/static/js/jsgrid/jsgrid-theme.min.css" >
-<link href="{{context}}/static/css/default.css" rel="stylesheet" type="text/css">
-<link href="{{context}}/static/css/fa/css/font-awesome.min.css" rel="stylesheet" type="text/css">
-<link rel="icon" href="data:;base64,iVBORw0KGgo=">
-
-<script src="{{ context }}/static/js/jquery/jquery.min.js"></script>
-<script src="{{context}}/static/js/chart.js/chart.bundle.js"></script>
-
-<script src="{{context}}/static/js/jx/rpc.js"></script>
-<script src="{{context}}/static/js/jx/dom.js"></script>
-<script src="{{context}}/static/js/jx/utils.js"></script>
-<script src="{{context}}/static/js/jx/ext/math.js"></script>
-<script src="{{ context }}/static/js/jsgrid/jsgrid.js"></script>
-<script src="{{context}}/static/js/colors.js"></script>
-<script src="{{context}}/static/js/dashboard.js"></script>
-<title>iMonitor</title>
-<script>
-	var HTTP_CONTEXT="{{context}}"
-	$(document).ready(function(){
-		monitor.folders.init()
-		monitor.processes.fetch()
-		
-	})
-</script>
-<body class="">
-	<div class="border-bottom caption" style="height:42px">
-		<div class="">{{title}}</div>
-		<div class="small" style="margin:4px">The Phi Technology LLC</div>
-	</div>
-	<i class="fa fa-reorder default left action" onclick="monitor.menu.event.toggle()"></i>
-	<div id="menuframe" class="left small " style="width:10%; height:90%">
-		 <div id="menu" class="menu"></div>
-	</div>
-	
-	<div class="left info">
-		
-		<div class="" style="height:28px; ">
-			<div class="left bold">Monitoring 
-			<span id="latest_processes_label" class="default bold"></span>
-			</div>
-			
-		</div>
-		
-		<div class = "shadow border-right" style="padding:2px; margin:4px; height:170px">	
-			<div id="latest_processes" class="grid" ></div>
-		</div>
-		<div style="height:22px; padding:2px" class="small">
-			<div id="latest_process_pager" align="center"></div>
-		</div>
-		<div id="process_summary" class=" grid border-right" style="margin:4px">
-			<div style="margin:4px; padding:2px; margin-bottom:4px; height:28px">
-				<div class="bold" >Application Summary By Status</div>
-				<div  id="app-summary-date" class="small"></div>
-			</div>
-			
-			<div class="simple-gradient shadow"  style="padding:2px; height:150px; margin:4px; margin-top:10px">
-				<div class="left small width-half" style="margin-top:10px">
-					<div class="border-right" style="padding:4px" title="Running"><span id="total-running" class="default">0</span> <i class="fa fa-check right" style="margin:4px"></i></div>
-					<div class="border-right"  title="Crash" style="padding:4px; margin-top:2px"><span id="total-crash" class="default">0</span> <i class="fa fa-times right" style="margin:4px"></i></div>
-					<div class="border-right"  title="Idle" style="padding:4px; margin-top:2px"><span id="total-idle" class="default">0</span> <i class="fa fa-ellipsis-h right" style="margin:4px"></i></div>
-					<div class="border-top"  style="padding:4px; margin-top:2px"> <span id="total-apps" class="default">0</span> <span class="right">Applications</span></div>
-				</div>
-				
-				<div id="summary_chart" class="width-half right"></div>
-			</div> 
-			<div id="summary_details" class="right"></div>
-		</div>
-		
-		<div class="grid border-right" style="margin:4px; margin-top:10px;">
-			<div style="height:28px">
-				<div class="bold" style="margin:4px; padding:4px">Application Summary By Groups</div>
-			</div>
-			<div class="shadow simple-gradient width" id="summary_ranking" style="margin:4px; padding:2px; text-transform:capitalize"></div>
-		</div>
-	</div>	
-	<div class="left info">
-		<div class="" style="height:28px">
-			<div class="small bold">CPU & Memory Usage Trend for <span id="trend_info" class="default bold"></span></div>
-			<div class="smal" style="float:none"><div id="has_anomaly" class="small"><i class="fa fa-warning" ></i> Anomaly Detected</div></div>
-
-		</div>
-		
-		<div class="shadow simple-gradient" style="height:270px; margin-top:4px">
-			<div id="trends_chart" class="small grid" style="height:250px"></div>
-		</div>
-		<div id="sandbox" class="border-top" style="padding:4px; margin-top:10px">
-			<div style="height:28px">
-				<div id="inspect_sandbox" class="right button border" style="display:none" onclick="monitor.sandbox.init()">Inspect</div>
-				<div class="bold">Python Virtual Environment Analysis</div>
-				
-				<div class="small">Last Lookup <span id="sandbox_date"></span></div>
-			</div>
-						
-			<div class="shadow " style="margin-top:10px; height:135px">
-				<div id="sandbox_status" class="">
-				</div>
-				<div id="sandbox_pager"></div>
-			</div>
-		</div>
-		<div style="margin-top:10px">
-			<div id="folder_summary">
-				<div style="height:28px">
-					
-					<div class="bold">Folder Analysis/Monitoring</div>
-					<div class="small">Powered By Machine Learning</div>
-				</div>
-				<div class="shadow">
-					<div class=" border-top" style="margin-top:4px; padding:2px; height:34px">
-						<i class="fa fa-search left" style="margin:4px; padding:4px; ; color:gray;"></i> 
-						<input id="folder_search" type="text" class="small left" placeholder="hostname" style="width:87%; padding-left:4px;" onkeyup="monitor.folders.search.init()"/>
-						<i class="fa fa-trash right action right" style="margin:4px; padding:4px; color:maroon" onclick="monitor.folders.search.reset()"></i>
-					</div>
-					<div style="margin-top:10px; height:170px">
-						<div id="gridfolders"></div>
-						<div id="folderspager" class="small" style="height:22px"></div>
-					</div>
-				</div>
-			</div>
-
-			<div id="folder_plan" style="display:none">
-				<div class="border-bottom" style="height:32px">
-					<div class="bold">Deletion/Archiving Plan 
-						<i class="fa fa-quote-left"></i> <span id="folder_name"></span> <i class="fa fa-quote-right"></i>
-						<i class="fa fa-angle-up action right bold" style="font-size:16px; margin:4px;" onclick="monitor.folders.show.grid()"></i>
-					</div>
-					<div class="small left">Powered By Machine Learning</div>
-					
-				</div>
-
-				<div id="delete_age" class="left width-half border-right" style="margin:2px; padding:2px;">
-					<div class="small" align="left">By Age</div>
-					<div class="number" style="height:42px">
-						
-						<div id="age_count"  align="right" class="left width-75" style="margin-right:4px">00</div>
-						<div class="small" class="left" style="height:100%; padding-top:15px">Files</div>
-							
-						
-					</div>
-					<div class="small border-top" align="center" style="padding-top:4px">
-						Approximately <span id="age_value">00</span> <span id="age_units"></span>
-					</div>
-					
-				</div>
-				<div id="delete_size" class="right width-half" class="number" style="margin:2px; padding:2px">
-					<div class="small" align="left">By Size</div>
-					
-					<div  class=" number" style="height:42px">
-						
-						<div id="size_count" align="right" class="left width-75" style="margin-right:4px">00</div>	
-						<div class="small" class="left" style="height:100%; padding-top:15px">Files</div>						
-					</div>
-					<div class="small border-top"align="center" style="padding-top:4px">
-						Approximately <span id="size_value">00</span> <span id="size_units"></span>
-					</div>
-					
-					
-				</div>
-			</div>
-			<div id="chartfolder" ></div>
-		</div>
-			
-		
-
-		
-	</div>
-	
-	
+<!doctype html>
+<html lang="en">
+<head>
+	<meta charset="utf-8" />
+	<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+
+	<title>Monitor</title>
+
+	<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0' name='viewport' />
+    <meta name="viewport" content="width=device-width" />
+
+
+    <!-- Bootstrap core CSS   -->
+    <link href="{{context}}/static/css/bootstrap.min.css" rel="stylesheet" />
+
+    <!-- Animation library for notifications   -->
+    <link href="{{context}}/static/css/animate.min.css" rel="stylesheet"/>
+
+    <!--  Dashboard core CSS    -->
+    <link href="{{context}}/static/css/dashboard.css" rel="stylesheet"/>
+
+    <!--  Fonts and icons     -->
+    <link href="http://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css" rel="stylesheet">
+    <!--
+        <link href='https://fonts.googleapis.com/css?family=Muli:400,300' rel='stylesheet' type='text/css'>
+    -->
+    <link href="{{context}}/static/css/themify-icons.css" rel="stylesheet">
+ <!--   Core JS Files   -->
+    <script src="{{context}}/static/js/jquery-1.10.2.js" type="text/javascript"></script>
+	<script src="{{context}}/static/js/bootstrap.min.js" type="text/javascript"></script>
+
+	<!--  Checkbox, Radio & Switch Plugins -->
+	<script src="{{context}}/static/js/bootstrap-checkbox-radio.js"></script>
+
+	<!--  Charts Plugin -->
+	<script src="{{context}}/static/js/chartist.min.js"></script>
+
+    <!--  Notifications Plugin    -->
+    <script src="{{context}}/static/js/bootstrap-notify.js"></script>
+
+    <!--  Google Maps Plugin
+        <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js"></script>
+    -->
+    <!-- Dashboard Core javascript and methods for Demo purpose -->
+	<script src="{{context}}/static/js/default.js"></script>
+
+	<!-- Dashboard DEMO methods, don't include it in your project! -->
+	<script src="{{context}}/static/js/dashboard.js"></script>
+    <script src="{{context}}/static/js/jx/dom.js"></script>
+    <script src="{{context}}/static/js/jx/rpc.js"></script>
+    <script src="{{context}}/static/js/jx/utils.js"></script>
+	<script type="text/javascript">
+        var URI_CONTEXT="{{context}}"
+    	$(document).ready(function(){
+
+            var lobservers = [
+                g.summary.factory('/1/get/summary/app_resources',function(r){
+
+                    //r = JSON.parse(r.responseText)
+                    jx.dom.set.value('total_cpu',r.cpu_usage)
+                    jx.dom.set.value('total_mem',r.memory_usage)
+                    jx.dom.set.value('mem_units',r.units)
+
+                }),
+                g.summary.factory('/1/get/summary/folder_size',function(r){
+                    //console.log(r.responseText)
+                    //r = JSON.parse(r.responseText)
+                    if (r.length == 0){
+                        r.size = "0.0"
+                        r.units = 'MB'
+                    }
+                    jx.dom.set.value('total_folder_size',r.size)
+                    jx.dom.set.value('folder_units',r.units)
+                }),
+                g.summary.factory('/1/get/summary/app_status',function(r){
+
+                    jx.dom.set.value('total_app_crashes',r.crash)
+                })
+            ]
+
+            jx.utils.patterns.observer(lobservers,"init")
+        	  dashboard.initChartist();
+
+    	});
+	</script>
+</head>
+
+
+
+<body>
+
+<div class="wrapper">
+    <div class="sidebar" data-background-color="white" data-active-color="danger">
+
+    <!--
+		Tip 1: you can change the color of the sidebar's background using: data-background-color="white | black"
+		Tip 2: you can change the color of the active button using the data-active-color="primary | info | success | warning | danger"
+	-->
+
+    	<div class="sidebar-wrapper">
+            <div class="logo">
+                <a href="#" class="simple-text">
+                    Monitor
+                </a>
+            </div>
+
+            <ul class="nav">
+                <li class="active">
+                    <a href="dashboard.html">
+                        <i class="ti-panel"></i>
+                        <p>Dashboard</p>
+                    </a>
+                </li>
+                <li>
+                    <a href= "/user">
+                        <i class="ti-user"></i>
+                        <p>User Profile</p>
+                    </a>
+                </li>
+				<li class="active-pro">
+                    <a href="/upgrade">
+                        <i class="ti-export"></i>
+                        <p>Upgrade to PRO</p>
+                    </a>
+                </li>
+            </ul>
+    	</div>
+    </div>
+    <!-- End Side Bar   -->
+    <div class="main-panel">
+        <nav class="navbar navbar-default">
+            <div class="container-fluid">
+                <div class="navbar-header">
+                    <button type="button" class="navbar-toggle">
+                        <span class="sr-only">Toggle navigation</span>
+                        <span class="icon-bar bar1"></span>
+                        <span class="icon-bar bar2"></span>
+                        <span class="icon-bar bar3"></span>
+                    </button>
+                    <a class="navbar-brand" href="#">Dashboard</a>
+                </div>
+                <div class="collapse navbar-collapse">
+                    <ul class="nav navbar-nav navbar-right">
+                        <li>
+                            <a href="#" class="dropdown-toggle" data-toggle="dropdown">
+                                <i class="ti-panel"></i>
+								<p>Stats</p>
+                            </a>
+                        </li>
+                        <li class="dropdown">
+                              <a href="#" class="dropdown-toggle" data-toggle="dropdown">
+                                    <i class="ti-server"></i>
+                                    <!-- <p class="notification">5</p> -->
+									<p>Servers</p>
+									<b class="caret"></b>
+                              </a>
+                              <ul class="dropdown-menu">
+                                  {% for name in app_names %}
+                                <div class="action" data-name="{{name}}" id="app_names"><i class="fa fa-angle-right"></i><a href='#'>{{name|safe}}</a>
+																	<script type="text/javascript">onclick=function(){
+																		console.log('name...', "{{ name }}") //this works, but returns same variable......
+
+																		var appName = $('#app_names').data("name");
+																		console.log('name...', appName )
+
+																	}</script>
+																</div>
+
+
+                                {% endfor %}
+                              </ul>
+                        </li>
+						<li>
+                            <a href="#">
+								<i class="ti-settings"></i>
+								<p>Settings</p>
+                            </a>
+                        </li>
+                    </ul>
+
+                </div>
+            </div>
+        </nav>
+        <!-- End Menu Bar -->
+
+        <div class="content">
+            <div class="container-fluid">
+                <div class="row">
+                    <div class="col-lg-3 col-sm-6">
+                        <div class="card">
+                            <div class="content">
+                                <div class="row">
+                                    <div class="col-xs-5">
+                                        <div class="icon-big icon-warning text-center">
+                                            <i class="ti-dashboard"></i>
+                                            <div class="small">Total CPU</div>
+                                        </div>
+                                    </div>
+                                    <div class="col-xs-7">
+
+                                        <div class="numbers">
+
+                                            <div id="total_cpu" align="center">00</div>
+                                            <div class="small"  align="right">Percent</div>
+                                        </div>
+
+                                    </div>
+                                </div>
+                                <div class="footer">
+                                    <hr />
+                                    <div class="stats">
+                                        <i class="ti-reload"></i> Updated now
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="col-lg-3 col-sm-6">
+                        <div class="card">
+                            <div class="content">
+                                <div class="row">
+                                    <div class="col-xs-5">
+                                        <div class="icon-big icon-success text-center">
+                                            <i class="fa fa-microchip"></i>
+                                            <div class="small">Mem. Used</div>
+
+                                        </div>
+                                    </div>
+                                    <div class="col-xs-7">
+                                        <div class="numbers">
+                                            <div id="total_mem"></div>
+                                            <div class="small" id="mem_units" align="right"></div>
+
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="footer">
+                                    <hr />
+                                    <div class="stats">
+                                        <i class="ti-calendar"></i> Last day
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="col-lg-3 col-sm-6">
+                        <div class="card">
+                            <div class="content">
+                                <div class="row">
+                                    <div class="col-xs-5">
+                                        <div class="icon-big icon-danger text-center">
+                                            <i class="fa fa-warning"></i>
+                                            <div class="small" align="center">Crashes</div>
+                                        </div>
+                                    </div>
+                                    <div class="col-xs-7">
+                                        <div class="numbers">
+                                            <div id="total_app_crashes"></div>
+                                            <!-- show errors API. Crashed? -->
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="footer">
+                                    <hr />
+                                    <div class="stats">
+                                        <i class="ti-timer"></i> In the last hour
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="col-lg-3 col-sm-6">
+                        <div class="card">
+                            <div class="content">
+                                <div class="row">
+                                    <div class="col-xs-5">
+                                        <div class="icon-big icon-info text-center">
+                                            <i class="ti-folder"></i>
+                                            <div class="small">Folders</div>
+                                        </div>
+                                    </div>
+                                    <div class="col-xs-7">
+                                        <div class="numbers">
+                                            <div id="total_folder_size" align="center"></div>
+                                            <div class="small" align="center" id="folder_units"></div>
+                                            <!-- Folder Analysis API here. -->
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="footer">
+                                    <hr />
+                                    <div class="stats">
+                                        <i class="ti-reload"></i> Updated now
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="row">
+
+                    <div class="col-md-12">
+                        <div class="card">
+                            <div class="header">
+                                <h4 class="title">Monitoring Apps</h4>
+                                <p class="category">performance</p>
+                            </div>
+                            <div class="content">
+                                <div id="chartHours" class="ct-chart"></div>
+
+                                <div class="footer">
+                                    <div class="chart-legend">
+                                        <i class="fa fa-circle text-info"></i> cpu usage
+                                        <i class="fa fa-circle text-warning"></i> memory used
+																				<i class="fa fa-circle text-danger"></i> available line
+                                    </div>
+                                    <hr>
+                                    <div class="stats">
+                                        <i class="ti-reload"></i> Updated 3 minutes ago
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="row">
+                    <div class="col-md-6">
+                        <div class="card">
+                            <div class="header">
+                                <h4 class="title">Summary</h4>
+                                <p class="category">Running|Idle|Crash</p>
+                            </div>
+                            <div class="content">
+                                <div id="chartPreferences" class="ct-chart ct-perfect-fourth"></div>
+
+                                <div class="footer">
+                                    <div class="chart-legend">
+                                        <i class="fa fa-circle text-info"></i> Running
+                                        <i class="fa fa-circle text-danger"></i> Crashed
+                                        <i class="fa fa-circle text-warning"></i> Idle
+                                    </div>
+                                    <hr>
+                                    <div class="stats">
+                                        <i class="ti-timer"></i> Some footer
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="col-md-6">
+                        <div class="card ">
+                            <div class="header">
+                                <h4 class="title">CPU and Memory</h4>
+                                <p class="category">Usage Trends</p>
+                            </div>
+                            <div class="content">
+                                <div id="chartActivity" class="ct-chart"></div>
+
+                                <div class="footer">
+                                    <div class="chart-legend">
+                                        <i class="fa fa-circle text-info"></i> CPU
+                                        <i class="fa fa-circle text-warning"></i> Memory
+                                    </div>
+                                    <hr>
+                                    <div class="stats">
+                                        <i class="ti-check"></i> Data information certified
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+
+        <footer class="footer">
+            <div class="container-fluid">
+                <nav class="pull-left">
+                    <ul>
+
+                        <li>
+                            <a href="#">
+                                Monitor
+                            </a>
+                        </li>
+                        <li>
+                            <a href="#">
+                               Invy
+                            </a>
+                        </li>
+                    </ul>
+                </nav>
+            </div>
+        </footer>
+    </div>
+</div>
+
 </body>
 </body>
+
+
+
+</html>

+ 1 - 0
src/api/templates/upgrade.html

@@ -0,0 +1 @@
+<H1>Upgrade</Hi>

+ 1 - 0
src/api/templates/user.html

@@ -0,0 +1 @@
+<H1>User</H1>

+ 4 - 1
src/monitor.py

@@ -30,6 +30,9 @@ class Analysis:
 		return {"month":d.month,"year":d.year, "day":d.day,"hour":d.hour,"minute":d.minute}
 		return {"month":d.month,"year":d.year, "day":d.day,"hour":d.hour,"minute":d.minute}
 	def getName(self):
 	def getName(self):
 		return self.__class__.__name__
 		return self.__class__.__name__
+	def cleanup(self,text):
+		return re.sub('[^a-zA-Z0-9\s:]',' ',str(text)).strip()
+		
 
 
 """
 """
 	This class is designed to analyze environment variables. Environment variables can either be folders, files or simple values
 	This class is designed to analyze environment variables. Environment variables can either be folders, files or simple values
@@ -214,7 +217,7 @@ class DetailProcess(Analysis):
 		else:
 		else:
 			return "crash"
 			return "crash"
 	def format(self,row):
 	def format(self,row):
-		r= {"memory_usage":row[0],"cpu_usage":row[1],"memory_available":row[2]/1000,"proc_count":row[3],"label":row[4]}
+		r= {"memory_usage":row[0],"cpu_usage":row[1],"memory_available":row[2]/1000,"proc_count":row[3],"label":self.cleanup(row[4])}
 		status = self.status(r)
 		status = self.status(r)
 		r['status'] = status
 		r['status'] = status
 		return r
 		return r

+ 303 - 0
src/utils/agents/actor.py

@@ -0,0 +1,303 @@
+"""
+	This class is designed to be an actor class i.e it will undertake certain actions given an event detected
+	The platform has 2 main sections (detection & analysis).
+	Action Types (Actors):
+		- Alert : Sends an email or Webhook
+		- Apps 	: Kill, Start
+		- Folder: Archive, Delete (all, age, size)
+        By design we are to understand that a message is structured as follows:
+            {to,from,content} with content either being an arbitrary stream (or JSON)
+	@TODO: 
+		- upgrade to python 3.x
+"""
+import json
+from threading import Thread
+import os
+import shutil
+import subprocess
+from monitor import ProcessCounter
+from utils.transport import QueueListener, QueueWriter, QueueReader
+from utils.params import PARAMS
+from ngram import NGram as ng
+class Actor(Thread):
+    def __init__(self):
+        Thread.__init__(self)
+        pass
+    def getIdentifier(self):
+        return self.__class__.__name__.lower()
+    """
+        Initializing the class with configuration. The configuration will be specific to each subclass
+
+    """
+    def init(self,config,item=None):
+        self.config = config
+        self.item = item
+	def process(self,item):
+		pass
+    def isValid(self,item):
+        return False
+
+    def execute(self,cmd):
+        stream = None
+        try:
+            subprocess.call (cmd,shell=False)
+            #stream = handler.communicate()[0]
+        except Exception,e:
+            pass
+        
+    def run(self):
+        if self.item is not None:
+            self.process(self.item)
+    """
+        Sending a message to a queue with parameters to,from,content
+    """
+    def post(self,**args):
+        pass
+"""
+    This is designed to handle folders i.e cleaning/archiving the folders
+    
+"""
+class Folders(Actor):
+    def init(self,config,item):
+        Actor.init(self,config,item)
+        self.lfolders   = config['folders']
+        self.config     = config['actions']['folders']
+        self.threshold  = self.get_size(self.config['threshold'])
+        self.item       = item
+    
+    def archive(self,item):
+    """
+        This function will archive all files in a given folder
+        @pre : isValid
+    """
+        folder = item['label']
+        signature='-'.join([str(item['date']),str(item['count']),'-files'])
+        tarball=os.sep([folder,signature])
+        shutil.make_archive(tarball,'tar',folder)
+        self.clean(item)
+        #
+        # @TODO: The archive can be uploaded to the cloud or else where
+        #   - This allows the submission of data to a processing engine if there ever were one
+        #
+        pass
+    
+    def clean(self,item):
+    """
+        This function consists in deleting files from a given folder
+    """
+        rpath = item['label']
+        lists = os.listdir(item['label'])
+        for name in list() :
+            path = os.sep([item['label'],name])
+            if os.path.isdir(path) :
+                shutil.rmtree(path)
+            else:
+                os.remove(path)
+        #
+        # 
+
+    def get_size(self,value):
+        units = {'MB':1000,'GB':1000000,'TB':1000000000} # converting to kb
+        key = set(unites) & set(re.split('(\d+)',value.upper()))
+        if len(key) == 0:
+            return -1
+        key = key.pop()
+        return float(value.upper().replace('MB','').strip()) * units[key]
+    
+    def isvalid(self,item):        
+    """
+        This function returns whether the following :
+        p : folder exists
+        q : has_reached threashold
+    """
+        
+        
+        p = os.path.exists(item['label']) and item['label'] in self.lfolders
+    
+        q = self.get_size(item['size']) >= self.threshold
+        return p and q
+    
+    def process(self,item):
+        if self.isValid(item) :
+            
+            name = self.config['action']
+            stream = "".join([name,'(',json.dumps(item),')'])
+            eval(stream)
+        
+        
+class Kill(Actor):
+    
+    def isValid(self,item):                
+        return (item is not None) and (item in self.config)
+	def process(self,item):
+		args = "".join(["-eo pid,command|grep ",item,'|grep -E "^ {0,1}[0-9]+" -o|xargs kill -9'])
+		self.execute(["ps",args])
+		#
+		# We need to make sure we can get assess the process on this server
+		#
+
+class Start(Actor):
+    def __init__(self):
+        Actor.__init__(self)
+        self.ng = None
+    
+    def init(self,config,item):
+        Actor.init(self,config,item)
+        self.config = config['apps']
+        self.ng = ng(self.config.keys())
+
+    def isValid(self,name):
+        items = self.ng.search(name) 
+        if len(items) == 0 :
+            return False
+        else:
+            return items[0][1] > 0.1
+                    
+    def process(self,row):
+        name    = row['label']
+        items   = self.ng.search(name)[0]
+        app = items[0]
+
+
+        args = self.config[app]
+        cmd = " ".join([app,args])
+        
+        self.execute([app,args])
+"""
+    This class is designed to handle applications i.e start/stopping applications
+    @TODO: Assess if a reboot is required, by looking at the variance/anomaly detection
+"""
+class Apps(Actor):
+    def __init__(self):
+        Actor.__init__(self)
+        self.crashes = []
+        self.running = []
+    
+    def isValid(self,rows):
+        status = [row['status'] for row in rows]
+        return 'crash' in status
+    
+    def classify(self,rows):
+        self.crashes = []
+        self.running = []
+        for row in rows:
+            if row['status'] == 'crash' :
+                self.crashes.append(row)
+            else:
+                self.running.append(row)
+    def reboot(self):
+        for row_run in self.running:
+            pass
+    def start(self):
+        for row_crash in self.crashes:
+            thread = Start()
+            thread.init(self.config,row_crash)
+            thread.daemon = True
+            thread.start()
+        
+    def process(self,rows):
+        self.classify(rows)
+        if self.crashes :
+            self.start()
+        if self.running:
+            self.reboot()
+        
+        
+class Event(Thread):    
+    def __init__(self,config):
+        pass
+    def run(self):
+"""
+    The orchestrator class is designed to aggregate actions and communicate back to the caller
+    Mesage passing is structured as follows {from,to,content} The content is designed to be understood by the actor
+    
+    The orchestrator is implemented using a simple iterator design-pattern
+    @TODO: action specifications should be provided remotely
+"""
+class Orchestrator(Actor):
+    
+    def __init__(self,config=None):
+        Actor.__init__(self)
+        if config is None:
+            f = open(PARAMS['path'])
+            config = json.loads(f.read())
+            f.close()
+        self.config = config
+        Actor.__init__(self)
+        
+        self.actors = {"apps":Apps(),"folders":Folders()}
+        self.is_master_node = False
+        self.items = []
+        #
+        # If the configuration only has id,key then this is NOT the maestro
+        #
+        host = config['api']
+        qid = config['id']
+        print "Initialized ***** ",self.getIdentifier(), " as ",config['id']
+        
+            #
+            # This object will have to request for the configuration
+            #
+        #for id in config['actions'] :
+            #conf = config['actions'][id]
+            #item = eval("".join([id,"(",json.dumps(conf),")"]))
+            #self.actors[id.lower()]  = item
+        """
+            This function is designed to provide the orchestrator a configuration             
+            @pre
+        """
+    def init(self,config):
+            
+        for id in config:
+            
+            setup_info  = config[id]
+            item        = eval("".join([id,"(",json.dumps(setup_info),")"]))
+            self.actors[id.lower()]  = item
+                
+    def callback(self,channel,method,header,stream):
+                
+        message = json.loads(stream)
+        if 'content' in message :
+            content = message['content']
+	    print self.actors        
+            to = message['to']
+            if isinstance(content,basestring) and content.lower() in ['quit'] or to=='quit':
+                if content.lower() == 'quit' or to == 'quit':
+                    print '**** closing ',self.getIdentifier()
+                    channel.close()
+            else:
+
+                id = to.lower()
+                actor = self.actors[id]
+                
+                if actor is not None and actor.isValid(content) :
+                    actor.init(self.config['actions'])                            
+                    actor.process(content)
+                else:
+                    content = {"status":"invalid","content":content}
+                
+                #self.post(to=sender,content=content)
+                
+    def run(self):
+
+        info = {}
+        host	= self.config['api']
+        uid	= self.config['key']
+        qid	= self.config['id']
+		
+        qlistener = QueueListener(qid=qid,uid=uid,host=host)		
+        qlistener.callback = self.callback
+        qlistener.read()
+        r = [self.process(item) for item in self.items]
+
+"""
+    This class is designed to send a message to a given AMQP enpoint
+    The AMQP endpoint is implemented by QueueWriter class
+"""
+# class Alert(Actor):
+#     def process(self,item):
+#         pass
+
+if __name__ == '__main__':
+    thread = Orchestrator()
+    thread.start()

+ 23 - 3
src/utils/agents/data-collector.py

@@ -27,6 +27,8 @@ class ICollector(Thread) :
 		self.factory	= DataSourceFactory()
 		self.factory	= DataSourceFactory()
 		self.init()
 		self.init()
 		self.name = 'data-collector@'+self.id
 		self.name = 'data-collector@'+self.id
+		
+		
 	def init(self):
 	def init(self):
 		
 		
 		
 		
@@ -51,7 +53,9 @@ class ICollector(Thread) :
 		self.quit = False
 		self.quit = False
 		#self.DELAY = PARAMS['delay']*60
 		#self.DELAY = PARAMS['delay']*60
 		self.DELAY = self.config['delay']
 		self.DELAY = self.config['delay']
-		
+		#
+		# we need to instanciate the actor orchestrator
+		#
 	"""
 	"""
 		This function returns an instance of a data collector class :
 		This function returns an instance of a data collector class :
 		ProcessDetails, FileWatch, ... provided the class name
 		ProcessDetails, FileWatch, ... provided the class name
@@ -70,6 +74,7 @@ class ICollector(Thread) :
 		write_class 	= self.config['store']['class']['write']
 		write_class 	= self.config['store']['class']['write']
 		read_args	= self.config['store']['args']
 		read_args	= self.config['store']['args']
 		DELAY	= self.config['delay'] * 60
 		DELAY	= self.config['delay'] * 60
+		
 		while self.quit == False:
 		while self.quit == False:
 			
 			
 			for thread in self.pool :
 			for thread in self.pool :
@@ -84,10 +89,25 @@ class ICollector(Thread) :
 				else:
 				else:
 					label = id
 					label = id
 					row = data
 					row = data
-
+				#
+				# At this point we should check for the status and if it prompts an action
+				# @TODO Use a design pattern for this ... (Aggregation?)
+				#   - submit the row to Event for analysis
+				#   - The event orchestrator will handle things from this point on
+				#
+				message = {}
+				
+				message['to'] = thread.getName()
+				message['content'] = row
+				qwriter = QueueWriter(host=self.config['api'],uid=self.config['key'],qid=self.id)
+				qwriter.write(label=self.id,row = message)
+				qwriter.close()
+				
 				self.lock.acquire()
 				self.lock.acquire()
 				store = self.factory.instance(type=write_class,args=read_args)
 				store = self.factory.instance(type=write_class,args=read_args)
+				
 				store.flush(size=200)
 				store.flush(size=200)
+				
 				store.write(label=label,row=row)
 				store.write(label=label,row=row)
 				self.lock.release()
 				self.lock.release()
 			if 'MONITOR_CONFIG_PATH' in os.environ :
 			if 'MONITOR_CONFIG_PATH' in os.environ :
@@ -104,4 +124,4 @@ class ICollector(Thread) :
 if __name__ == '__main__':
 if __name__ == '__main__':
 	thread = ICollector()
 	thread = ICollector()
 	# thread.daemon = True
 	# thread.daemon = True
-	thread.start()
+	thread.start()

+ 35 - 8
src/utils/transport.py

@@ -261,6 +261,7 @@ class MessageQueue:
 		self.close()
 		self.close()
 		return resp
 		return resp
 	def close(self):
 	def close(self):
+            if self.connection.is_closed == False :
 		self.channel.close()
 		self.channel.close()
 		self.connection.close()
 		self.connection.close()
 """
 """
@@ -345,9 +346,12 @@ class QueueReader(MessageQueue,Reader):
 		#self.uid = params['uid']
 		#self.uid = params['uid']
 		#self.qid = params['qid']
 		#self.qid = params['qid']
 		MessageQueue.__init__(self,**params);
 		MessageQueue.__init__(self,**params);
+		if 'durable' in params :
+			self.durable = True
+		else:
+			self.durable = False
 		self.size = -1
 		self.size = -1
 		self.data = {}
 		self.data = {}
-	
 	def init(self,qid):
 	def init(self,qid):
 		
 		
 		properties = pika.ConnectionParameters(host=self.host)
 		properties = pika.ConnectionParameters(host=self.host)
@@ -364,6 +368,7 @@ class QueueReader(MessageQueue,Reader):
 
 
 	"""
 	"""
 	def callback(self,channel,method,header,stream):
 	def callback(self,channel,method,header,stream):
+                
 		r = []
 		r = []
 		if re.match("^\{|\[",stream) is not None:
 		if re.match("^\{|\[",stream) is not None:
 			r = json.loads(stream)
 			r = json.loads(stream)
@@ -395,9 +400,12 @@ class QueueReader(MessageQueue,Reader):
 		# We enabled the reader to be able to read from several queues (sequentially for now)
 		# We enabled the reader to be able to read from several queues (sequentially for now)
 		# The qid parameter will be an array of queues the reader will be reading from
 		# The qid parameter will be an array of queues the reader will be reading from
 		#
 		#
+		if isinstance(self.qid,basestring) :
+                    self.qid = [self.qid]
 		for qid in self.qid:
 		for qid in self.qid:
 			self.init(qid)
 			self.init(qid)
 			# r[qid] = []
 			# r[qid] = []
+			
 			if self.info.method.message_count > 0:
 			if self.info.method.message_count > 0:
 				
 				
 				self.channel.basic_consume(self.callback,queue=qid,no_ack=False);
 				self.channel.basic_consume(self.callback,queue=qid,no_ack=False);
@@ -406,11 +414,26 @@ class QueueReader(MessageQueue,Reader):
 				
 				
 				pass
 				pass
 				#self.close()
 				#self.close()
-			
 			# r[qid].append( self.data)
 			# r[qid].append( self.data)
 		
 		
 		return self.data
 		return self.data
-		
+class QueueListener(QueueReader):
+	def init(self,qid):
+		properties = pika.ConnectionParameters(host=self.host)
+		self.connection = pika.BlockingConnection(properties)
+		self.channel	= self.connection.channel()
+		self.channel.exchange_declare(exchange=self.uid,type='direct',durable=True )
+
+		self.info = self.channel.queue_declare(passive=True,exclusive=True,queue=qid)
+		
+		self.channel.queue_bind(exchange=self.uid,queue=self.info.method.queue,routing_key=qid)
+		#self.callback = callback
+	def read(self):
+    	
+		self.init(self.qid)
+		self.channel.basic_consume(self.callback,queue=self.qid,no_ack=True);
+		self.channel.start_consuming()
+    		
 """
 """
 	This class is designed to write output as sql insert statements
 	This class is designed to write output as sql insert statements
 	The class will inherit from DiskWriter with minor adjustments
 	The class will inherit from DiskWriter with minor adjustments
@@ -465,7 +488,11 @@ class Couchdb:
 		if q == False:
 		if q == False:
 			return False
 			return False
 		return True
 		return True
-	
+	def view(self,id,**args):
+		r =self.dbase.view(id,**args)
+		r = r.all()		
+		return r[0]['value'] if len(r) > 0 else []
+		
 """
 """
 	This function will read an attachment from couchdb and return it to calling code. The attachment must have been placed before hand (otherwise oops)
 	This function will read an attachment from couchdb and return it to calling code. The attachment must have been placed before hand (otherwise oops)
 	@T: Account for security & access control
 	@T: Account for security & access control
@@ -564,7 +591,7 @@ class CouchdbWriter(Couchdb,Writer):
 		self.dbase.save_doc(document)
 		self.dbase.save_doc(document)
 	def flush(self,**params) :
 	def flush(self,**params) :
 		
 		
-		size = params['size']
+		size = params['size'] if 'size' in params else 0
 		has_changed = False	
 		has_changed = False	
 		document = self.dbase.get(self.uid)
 		document = self.dbase.get(self.uid)
 		for key in document:
 		for key in document:
@@ -572,7 +599,7 @@ class CouchdbWriter(Couchdb,Writer):
 				content = document[key]
 				content = document[key]
 			else:
 			else:
 				continue
 				continue
-			if isinstance(content,list):
+			if isinstance(content,list) and size > 0:
 				index = len(content) - size
 				index = len(content) - size
 				content = content[index:]
 				content = content[index:]
 				document[key] = content
 				document[key] = content
@@ -580,8 +607,8 @@ class CouchdbWriter(Couchdb,Writer):
 			else:
 			else:
 				document[key] = {}
 				document[key] = {}
 				has_changed = True
 				has_changed = True
-		if has_changed:
-			self.dbase.save_doc(document)
+		
+		self.dbase.save_doc(document)
 			
 			
 	def archive(self,params=None):
 	def archive(self,params=None):
 		document = self.dbase.get(self.uid)
 		document = self.dbase.get(self.uid)

+ 8 - 0
start.sh

@@ -0,0 +1,8 @@
+#!/bin/bash
+
+#script_dir=`dirname $0`
+#cd $script_dir
+#/bin/bash -c ". activate sandbox; exec /bin/bash -i"
+`source activate sandbox`
+export PYTHONPATH=$PWD/src
+python src/utils/agents/data-collector.py --path /Users/michaelmead/Documents/Programming/monitor/config.json --title "Seekers Dashboard"