Parcourir la source

Merge branch 'graphs' of steve/monitor into master

Steve L. Nyemba il y a 8 ans
Parent
commit
8d4f895bea
55 fichiers modifiés avec 22814 ajouts et 1119 suppressions
  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
 *.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":{
 		"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"]
+			}
+	}
+	
+}

Fichier diff supprimé car celui-ci est trop grand
+ 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
 
 git clone https://github.com/lnyemba/jx src/api/static/js/jx
-virtualenv sandbox
+virtualenv sandbox --python=python2.7
 source sandbox/bin/activate
 pip install -r requirements.txt
 export PYTHONPATH=$PWD/src

+ 14 - 0
readme.md

@@ -6,6 +6,20 @@ The program answers basic questions:
 	- Is a given program still running
 	- How much resource (memory/cpu) a program is using up
 	- 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 :
 

+ 9 - 6
requirements.txt

@@ -1,13 +1,16 @@
-aniso8601==1.2.0
-click==6.6
-couchdbkit==0.6.5
 Flask==0.11.1
 Flask-Session==0.3.0
 Flask-SocketIO==2.8.2
-http-parser==0.8.3
-itsdangerous==0.24
 Jinja2==2.8
 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
 pika==0.10.0
 python-dateutil==2.6.0
@@ -17,4 +20,4 @@ pytz==2016.10
 restkit==4.2.2
 six==1.10.0
 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.
 	The API is driven by configuration that is organized in terms of the monitoring classes
-	
+
 	The API is both restful and websocket/socketio enabled.
 
 	We designed the classes to be reusable (and powered by labels):
 	'monitoring-type':
 		'class':'<class-name>'
 		'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
@@ -45,51 +48,180 @@ class_write= CONFIG['store']['class']['write']
 factory = DataSourceFactory()
 # 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:
 		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:
+		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
-		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
@@ -98,7 +230,7 @@ def procs(id):
 @app.route('/sandbox')
 def sandbox():
 	global CONFIG
-	
+
 	if 'sandbox' in CONFIG: #CONFIG['monitor']:
 		#handler = HANDLERS['sandbox']['class']
 		#conf = HANDLERS['sandbox']['config']
@@ -106,7 +238,7 @@ def sandbox():
 		# p = Factory.instance('sandbox',CONFIG)
 		handler = monitor.Sandbox()
 		conf	= CONFIG['sandbox']
-		
+
 		for id in conf:
 			try:
 				handler.init(conf[id])
@@ -114,182 +246,207 @@ def sandbox():
 			except Exception,e:
 				pass
 	else:
-		
+
 		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')
 def dashboard():
 	context = PARAMS['context']
 	if 'title' in PARAMS :
 		title = PARAMS['title']
 	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
 	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__':
-	
-#	ThreadManager.start(CONFIG)	
+
+#	ThreadManager.start(CONFIG)
 	if 'port' not in SYS_ARGS.PARAMS :
-		SYS_ARGS.PARAMS['port'] = 5000
+		SYS_ARGS.PARAMS['port'] = 8484
 	PORT = int(SYS_ARGS.PARAMS['port'])
 	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 */

Fichier diff supprimé car celui-ci est trop grand
+ 10 - 0
src/api/static/chartist/chartist.css.map


Fichier diff supprimé car celui-ci est trop grand
+ 4488 - 0
src/api/static/chartist/chartist.js


Fichier diff supprimé car celui-ci est trop grand
+ 1 - 0
src/api/static/chartist/chartist.min.css


Fichier diff supprimé car celui-ci est trop grand
+ 10 - 0
src/api/static/chartist/chartist.min.js


Fichier diff supprimé car celui-ci est trop grand
+ 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;

Fichier diff supprimé car celui-ci est trop grand
+ 6 - 0
src/api/static/css/animate.min.css


Fichier diff supprimé car celui-ci est trop grand
+ 5 - 0
src/api/static/css/bootstrap.min.css


Fichier diff supprimé car celui-ci est trop grand
+ 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;
   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
-  text-rendering: auto; // optimizelegibility throws things off #1094
+  text-rendering: auto; // optimize legibility throws things off #1094
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
 

Fichier diff supprimé car celui-ci est trop grand
+ 1081 - 0
src/api/static/css/themify-icons.css


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


Fichier diff supprimé car celui-ci est trop grand
+ 362 - 0
src/api/static/fonts/themify.svg


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


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


Fichier diff supprimé car celui-ci est trop grand
+ 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);

Fichier diff supprimé car celui-ci est trop grand
+ 404 - 0
src/api/static/js/bootstrap-notify.js


Fichier diff supprimé car celui-ci est trop grand
+ 7 - 0
src/api/static/js/bootstrap.min.js


Fichier diff supprimé car celui-ci est trop grand
+ 9 - 0
src/api/static/js/chartist.min.js


Fichier diff supprimé car celui-ci est trop grand
+ 216 - 0
src/api/static/js/chartist.min.js.map


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 2
src/api/static/js/colors.js


Fichier diff supprimé car celui-ci est trop grand
+ 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);
+	};
+};

Fichier diff supprimé car celui-ci est trop grand
+ 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>
+
+
+
+</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}
 	def getName(self):
 		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
@@ -214,7 +217,7 @@ class DetailProcess(Analysis):
 		else:
 			return "crash"
 	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)
 		r['status'] = status
 		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.init()
 		self.name = 'data-collector@'+self.id
+		
+		
 	def init(self):
 		
 		
@@ -51,7 +53,9 @@ class ICollector(Thread) :
 		self.quit = False
 		#self.DELAY = PARAMS['delay']*60
 		self.DELAY = self.config['delay']
-		
+		#
+		# we need to instanciate the actor orchestrator
+		#
 	"""
 		This function returns an instance of a data collector class :
 		ProcessDetails, FileWatch, ... provided the class name
@@ -70,6 +74,7 @@ class ICollector(Thread) :
 		write_class 	= self.config['store']['class']['write']
 		read_args	= self.config['store']['args']
 		DELAY	= self.config['delay'] * 60
+		
 		while self.quit == False:
 			
 			for thread in self.pool :
@@ -84,10 +89,25 @@ class ICollector(Thread) :
 				else:
 					label = id
 					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()
 				store = self.factory.instance(type=write_class,args=read_args)
+				
 				store.flush(size=200)
+				
 				store.write(label=label,row=row)
 				self.lock.release()
 			if 'MONITOR_CONFIG_PATH' in os.environ :
@@ -104,4 +124,4 @@ class ICollector(Thread) :
 if __name__ == '__main__':
 	thread = ICollector()
 	# thread.daemon = True
-	thread.start()
+	thread.start()

+ 35 - 8
src/utils/transport.py

@@ -261,6 +261,7 @@ class MessageQueue:
 		self.close()
 		return resp
 	def close(self):
+            if self.connection.is_closed == False :
 		self.channel.close()
 		self.connection.close()
 """
@@ -345,9 +346,12 @@ class QueueReader(MessageQueue,Reader):
 		#self.uid = params['uid']
 		#self.qid = params['qid']
 		MessageQueue.__init__(self,**params);
+		if 'durable' in params :
+			self.durable = True
+		else:
+			self.durable = False
 		self.size = -1
 		self.data = {}
-	
 	def init(self,qid):
 		
 		properties = pika.ConnectionParameters(host=self.host)
@@ -364,6 +368,7 @@ class QueueReader(MessageQueue,Reader):
 
 	"""
 	def callback(self,channel,method,header,stream):
+                
 		r = []
 		if re.match("^\{|\[",stream) is not None:
 			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)
 		# 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:
 			self.init(qid)
 			# r[qid] = []
+			
 			if self.info.method.message_count > 0:
 				
 				self.channel.basic_consume(self.callback,queue=qid,no_ack=False);
@@ -406,11 +414,26 @@ class QueueReader(MessageQueue,Reader):
 				
 				pass
 				#self.close()
-			
 			# r[qid].append( 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
 	The class will inherit from DiskWriter with minor adjustments
@@ -465,7 +488,11 @@ class Couchdb:
 		if q == False:
 			return False
 		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)
 	@T: Account for security & access control
@@ -564,7 +591,7 @@ class CouchdbWriter(Couchdb,Writer):
 		self.dbase.save_doc(document)
 	def flush(self,**params) :
 		
-		size = params['size']
+		size = params['size'] if 'size' in params else 0
 		has_changed = False	
 		document = self.dbase.get(self.uid)
 		for key in document:
@@ -572,7 +599,7 @@ class CouchdbWriter(Couchdb,Writer):
 				content = document[key]
 			else:
 				continue
-			if isinstance(content,list):
+			if isinstance(content,list) and size > 0:
 				index = len(content) - size
 				content = content[index:]
 				document[key] = content
@@ -580,8 +607,8 @@ class CouchdbWriter(Couchdb,Writer):
 			else:
 				document[key] = {}
 				has_changed = True
-		if has_changed:
-			self.dbase.save_doc(document)
+		
+		self.dbase.save_doc(document)
 			
 	def archive(self,params=None):
 		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"