lib2chstat integration, refactoring

This commit is contained in:
fadedDexofan
2018-10-30 22:55:36 +10:00
parent bb57b8fab3
commit 387009b81e
39 changed files with 1192 additions and 22040 deletions

13
.idea/dataSources.local.xml generated Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="dataSourceStorageLocal">
<data-source name="main" uuid="b21434bf-adba-4759-9543-cb70d5246907">
<database-info product="SQLite" version="3.25.1" jdbc-version="2.1" driver-name="SQLite JDBC" driver-version="3.25.1" family="SQLITE" exact-version="3.25.1">
<identifier-quote-string>&quot;</identifier-quote-string>
</database-info>
<case-sensitivity plain-identifiers="mixed" quoted-identifiers="mixed" />
<auth-required>false</auth-required>
<introspection-schemas>*:@</introspection-schemas>
</data-source>
</component>
</project>

22
.idea/dataSources.xml generated Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="main" uuid="b21434bf-adba-4759-9543-cb70d5246907">
<driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:C:\Users\faded\PycharmProjects\libopenanal\main.db</jdbc-url>
<driver-properties>
<property name="enable_load_extension" value="true" />
</driver-properties>
<libraries>
<library>
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.25.1/license.txt</url>
</library>
<library>
<url>file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.25.1/sqlite-jdbc-3.25.1.jar</url>
</library>
</libraries>
</data-source>
</component>
</project>

View File

@ -0,0 +1,220 @@
<?xml version="1.0" encoding="UTF-8"?>
<dataSource name="main">
<database-model serializer="dbm" rdbms="SQLITE" format-version="4.11">
<root id="1">
<ServerVersion>3.25.1</ServerVersion>
</root>
<schema id="2" parent="1" name="main">
<Current>1</Current>
<Visible>1</Visible>
</schema>
<collation id="3" parent="1" name="BINARY"/>
<collation id="4" parent="1" name="NOCASE"/>
<collation id="5" parent="1" name="RTRIM"/>
<table id="6" parent="2" name="conf"/>
<table id="7" parent="2" name="relations"/>
<table id="8" parent="2" name="reset"/>
<table id="9" parent="2" name="sqlite_master">
<System>1</System>
</table>
<table id="10" parent="2" name="sqlite_sequence">
<System>1</System>
</table>
<table id="11" parent="2" name="user"/>
<table id="12" parent="2" name="word"/>
<column id="13" parent="6" name="id">
<Position>1</Position>
<DataType>NUMERIC|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="14" parent="6" name="title">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="15" parent="6" name="date">
<Position>3</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
</column>
<index id="16" parent="6" name="sqlite_autoindex_conf_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>id</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="17" parent="6">
<ColNames>id</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_conf_1</UnderlyingIndexName>
</key>
<key id="18" parent="6">
<ColNames>id</ColNames>
<UnderlyingIndexName>sqlite_autoindex_conf_1</UnderlyingIndexName>
</key>
<column id="19" parent="7" name="id">
<Position>1</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
<SequenceIdentity>1</SequenceIdentity>
</column>
<column id="20" parent="7" name="word_id">
<Position>2</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="21" parent="7" name="user_id">
<Position>3</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="22" parent="7" name="conf_id">
<Position>4</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="23" parent="7" name="date">
<Position>5</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
</column>
<key id="24" parent="7">
<ColNames>id</ColNames>
<Primary>1</Primary>
</key>
<foreign-key id="25" parent="7">
<ColNames>word_id</ColNames>
<RefTableName>word</RefTableName>
<RefColNames>id</RefColNames>
<OnDelete>cascade</OnDelete>
</foreign-key>
<foreign-key id="26" parent="7">
<ColNames>user_id</ColNames>
<RefTableName>user</RefTableName>
<RefColNames>id</RefColNames>
</foreign-key>
<foreign-key id="27" parent="7">
<ColNames>conf_id</ColNames>
<RefTableName>conf</RefTableName>
<RefColNames>id</RefColNames>
</foreign-key>
<column id="28" parent="8" name="id">
<Position>1</Position>
<DataType>INTEGER|0s</DataType>
<SequenceIdentity>1</SequenceIdentity>
</column>
<column id="29" parent="8" name="user_id">
<Position>2</Position>
<DataType>INTEGER|0s</DataType>
</column>
<column id="30" parent="8" name="conf_id">
<Position>3</Position>
<DataType>INTEGER|0s</DataType>
</column>
<column id="31" parent="8" name="date">
<Position>4</Position>
<DataType>INTEGER|0s</DataType>
</column>
<column id="32" parent="8" name="relation_id">
<Position>5</Position>
<DataType>INTEGER|0s</DataType>
</column>
<key id="33" parent="8">
<ColNames>id</ColNames>
<Primary>1</Primary>
</key>
<foreign-key id="34" parent="8">
<ColNames>user_id</ColNames>
<RefTableName>user</RefTableName>
<RefColNames>id</RefColNames>
</foreign-key>
<column id="35" parent="9" name="type">
<Position>1</Position>
<DataType>text|0s</DataType>
</column>
<column id="36" parent="9" name="name">
<Position>2</Position>
<DataType>text|0s</DataType>
</column>
<column id="37" parent="9" name="tbl_name">
<Position>3</Position>
<DataType>text|0s</DataType>
</column>
<column id="38" parent="9" name="rootpage">
<Position>4</Position>
<DataType>int|0s</DataType>
</column>
<column id="39" parent="9" name="sql">
<Position>5</Position>
<DataType>text|0s</DataType>
</column>
<column id="40" parent="10" name="name">
<Position>1</Position>
</column>
<column id="41" parent="10" name="seq">
<Position>2</Position>
</column>
<column id="42" parent="11" name="id">
<Position>1</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="43" parent="11" name="username">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="44" parent="11" name="first_name">
<Position>3</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="45" parent="11" name="last_name">
<Position>4</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="46" parent="11" name="date">
<Position>5</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
</column>
<index id="47" parent="11" name="sqlite_autoindex_user_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>id</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="48" parent="11">
<ColNames>id</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_user_1</UnderlyingIndexName>
</key>
<key id="49" parent="11">
<ColNames>id</ColNames>
<UnderlyingIndexName>sqlite_autoindex_user_1</UnderlyingIndexName>
</key>
<column id="50" parent="12" name="id">
<Position>1</Position>
<DataType>INTEGER|0s</DataType>
<SequenceIdentity>1</SequenceIdentity>
</column>
<column id="51" parent="12" name="word">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
</column>
<index id="52" parent="12" name="sqlite_autoindex_word_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>word</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="53" parent="12">
<ColNames>id</ColNames>
<Primary>1</Primary>
</key>
<key id="54" parent="12">
<ColNames>word</ColNames>
<UnderlyingIndexName>sqlite_autoindex_word_1</UnderlyingIndexName>
</key>
</database-model>
</dataSource>

View File

@ -0,0 +1,2 @@
#n:main
!<md> [0, 0, null, null, -2147483648, -2147483648]

19
.idea/libopenanal.iml generated Normal file
View File

@ -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$/templates" />
</list>
</option>
</component>
<component name="TestRunnerService">
<option name="PROJECT_TEST_RUNNER" value="Unittests" />
</component>
</module>

7
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.7 (venv)" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

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

6
.idea/sqldialects.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="PROJECT" dialect="SQLite" />
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@ -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>

445
.idea/workspace.xml generated Normal file
View File

@ -0,0 +1,445 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="6413bbea-2b6d-40f8-9441-47ad6c6cb32c" name="Default Changelist" comment="">
<change afterPath="$PROJECT_DIR$/.idea/sqldialects.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/templates/base.html" afterDir="false" />
<change afterPath="$PROJECT_DIR$/templates/stat.html" afterDir="false" />
<change beforePath="$PROJECT_DIR$/database.py" beforeDir="false" afterPath="$PROJECT_DIR$/database.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/index.py" beforeDir="false" afterPath="$PROJECT_DIR$/index.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/settings.py" beforeDir="false" afterPath="$PROJECT_DIR$/settings.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/static/css/bootstrap-grid.css" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/css/bootstrap-grid.css.map" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/css/bootstrap-grid.min.css" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/css/bootstrap-grid.min.css.map" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/css/bootstrap-reboot.css" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/css/bootstrap-reboot.css.map" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/css/bootstrap-reboot.min.css" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/css/bootstrap-reboot.min.css.map" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/css/bootstrap.css" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/css/bootstrap.css.map" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/css/bootstrap.min.css" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/css/bootstrap.min.css.map" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/js/bootstrap.bundle.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/js/bootstrap.bundle.js.map" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/js/bootstrap.bundle.min.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/js/bootstrap.bundle.min.js.map" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/js/bootstrap.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/js/bootstrap.js.map" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/js/bootstrap.min.js" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/static/js/bootstrap.min.js.map" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/templates/conf.html" beforeDir="false" afterPath="$PROJECT_DIR$/templates/conf.html" afterDir="false" />
<change beforePath="$PROJECT_DIR$/templates/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/templates/index.html" afterDir="false" />
<change beforePath="$PROJECT_DIR$/templates/user.html" beforeDir="false" afterPath="$PROJECT_DIR$/templates/user.html" afterDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" 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="CoverageDataManager">
<SUITE FILE_PATH="coverage/libopenanal$index.coverage" NAME="index Coverage Results" MODIFIED="1540904061332" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
</component>
<component name="DatabaseView">
<option name="SHOW_INTERMEDIATE" value="true" />
<option name="GROUP_DATA_SOURCES" value="true" />
<option name="GROUP_SCHEMA" value="true" />
<option name="GROUP_CONTENTS" value="false" />
<option name="SORT_POSITIONED" value="false" />
<option name="SHOW_EMPTY_GROUPS" value="false" />
<option name="AUTO_SCROLL_FROM_SOURCE" value="false" />
<option name="HIDDEN_KINDS">
<set />
</option>
<expand />
<select />
</component>
<component name="FUSProjectUsageTrigger">
<session id="1279820601">
<usages-collector id="statistics.lifecycle.project">
<counts>
<entry key="project.open.time.1" value="1" />
<entry key="project.opened" value="1" />
</counts>
</usages-collector>
<usages-collector id="statistics.file.extensions.open">
<counts>
<entry key="gitignore" value="1" />
<entry key="html" value="5" />
<entry key="py" value="3" />
</counts>
</usages-collector>
<usages-collector id="statistics.file.types.open">
<counts>
<entry key="Git file" value="1" />
<entry key="HTML" value="5" />
<entry key="Python" value="3" />
</counts>
</usages-collector>
<usages-collector id="statistics.file.extensions.edit">
<counts>
<entry key="html" value="179" />
<entry key="py" value="66" />
</counts>
</usages-collector>
<usages-collector id="statistics.file.types.edit">
<counts>
<entry key="HTML" value="179" />
<entry key="Python" value="65" />
<entry key="SQL" value="1" />
</counts>
</usages-collector>
</session>
</component>
<component name="FileEditorManager">
<leaf>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/.gitignore">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="168">
<caret line="8" selection-start-line="8" selection-end-line="8" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/templates/base.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="194">
<caret line="18" selection-start-line="18" selection-end-line="18" selection-end-column="19" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/templates/index.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="488">
<caret line="77" column="14" selection-start-line="77" selection-start-column="14" selection-end-line="77" selection-end-column="14" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/templates/stat.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="509">
<caret line="78" column="14" selection-start-line="78" selection-start-column="14" selection-end-line="78" selection-end-column="14" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/templates/conf.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="467">
<caret line="59" column="14" lean-forward="true" selection-start-line="59" selection-start-column="14" selection-end-line="59" selection-end-column="14" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/templates/user.html">
<provider selected="true" editor-type-id="text-editor">
<state>
<caret selection-end-line="20" selection-end-column="4" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/settings.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="126">
<caret line="6" column="17" selection-start-line="6" selection-start-column="17" selection-end-line="6" selection-end-column="17" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/index.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="2667">
<caret line="127" column="32" lean-forward="true" selection-start-line="127" selection-start-column="32" selection-end-line="127" selection-end-column="32" />
<folding>
<element signature="e#0#15#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/database.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-1906">
<caret line="69" column="16" selection-start-line="69" selection-start-column="16" selection-end-line="69" selection-end-column="16" />
<folding>
<element signature="e#0#14#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="HTML File" />
</list>
</option>
</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$/database.py" />
<option value="$PROJECT_DIR$/index.py" />
<option value="$PROJECT_DIR$/templates/base.html" />
<option value="$PROJECT_DIR$/templates/user.html" />
<option value="$PROJECT_DIR$/templates/conf.html" />
<option value="$PROJECT_DIR$/templates/stat.html" />
<option value="$PROJECT_DIR$/templates/index.html" />
<option value="$PROJECT_DIR$/settings.py" />
</list>
</option>
</component>
<component name="JsBuildToolGruntFileManager" detection-done="true" sorting="DEFINITION_ORDER" />
<component name="JsBuildToolPackageJson" detection-done="true" sorting="DEFINITION_ORDER" />
<component name="JsFlowSettings">
<service-enabled>true</service-enabled>
<exe-path />
<other-services-enabled>true</other-services-enabled>
<auto-save>true</auto-save>
</component>
<component name="JsGulpfileManager">
<detection-done>true</detection-done>
<sorting>DEFINITION_ORDER</sorting>
</component>
<component name="ProjectFrameBounds" extendedState="6">
<option name="x" value="675" />
<option name="y" value="55" />
<option name="width" value="1936" />
<option name="height" value="1066" />
</component>
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="Scope" />
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="libopenanal" type="b2602c69:ProjectViewProjectNode" />
<item name="libopenanal" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="libopenanal" type="b2602c69:ProjectViewProjectNode" />
<item name="libopenanal" type="462c0819:PsiDirectoryNode" />
<item name="templates" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
</panes>
</component>
<component name="PropertiesComponent">
<property name="DefaultHtmlFileTemplate" value="HTML File" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="add_unversioned_files" value="true" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" />
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
<property name="settings.editor.selected.configurable" value="preferences.externalTools" />
</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 name="index" type="PythonConfigurationType" factoryName="Python" temporary="true">
<module name="libopenanal" />
<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="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/index.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration default="true" type="Python.FlaskServer" factoryName="Flask server">
<option name="additionalOptions" value="" />
<option name="application" value="" />
<module name="libopenanal" />
<option name="target" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<option name="SDK_HOME" value="C:\Users\faded\.virtualenvs\lib2chstat-jAArTfmX\Scripts\python.exe" />
<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" runner="coverage.py" />
<option name="launchJavascriptDebuger" value="false" />
<method v="2" />
</configuration>
<recent_temporary>
<list>
<item itemvalue="Python.index" />
</list>
</recent_temporary>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="6413bbea-2b6d-40f8-9441-47ad6c6cb32c" name="Default Changelist" comment="" />
<created>1540901950817</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1540901950817</updated>
</task>
<servers />
</component>
<component name="TodoView">
<todo-panel id="selected-file">
<is-autoscroll-to-source value="true" />
</todo-panel>
<todo-panel id="all">
<are-packages-shown value="true" />
<is-autoscroll-to-source value="true" />
</todo-panel>
</component>
<component name="ToolWindowManager">
<frame x="-8" y="22" width="1936" height="1066" extended-state="6" />
<editor active="true" />
<layout>
<window_info id="Favorites" order="0" side_tool="true" />
<window_info active="true" content_ui="combo" id="Project" order="1" visible="true" weight="0.2496" />
<window_info id="Structure" order="2" side_tool="true" weight="0.25" />
<window_info anchor="bottom" id="Database Changes" order="0" />
<window_info anchor="bottom" id="Terminal" order="1" weight="0.3290461" />
<window_info anchor="bottom" id="Event Log" order="2" side_tool="true" />
<window_info anchor="bottom" id="Version Control" order="3" show_stripe_button="false" />
<window_info anchor="bottom" id="Python Console" order="4" />
<window_info anchor="bottom" id="Docker" order="5" show_stripe_button="false" />
<window_info anchor="bottom" id="Message" order="6" />
<window_info anchor="bottom" id="Find" order="7" />
<window_info anchor="bottom" id="Run" order="8" visible="true" weight="0.3290461" />
<window_info anchor="bottom" id="Debug" order="9" weight="0.4" />
<window_info anchor="bottom" id="Cvs" order="10" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="11" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="12" weight="0.3290461" />
<window_info anchor="right" id="Database" order="0" visible="true" weight="0.3296" />
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="1" type="SLIDING" weight="0.4" />
<window_info anchor="right" id="SciView" order="2" />
<window_info anchor="right" id="Ant Build" order="3" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="4" weight="0.25" />
</layout>
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="1" />
</component>
<component name="VcsContentAnnotationSettings">
<option name="myLimit" value="2678400000" />
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/database.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-1906">
<caret line="69" column="16" selection-start-line="69" selection-start-column="16" selection-end-line="69" selection-end-column="16" />
<folding>
<element signature="e#0#14#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/.gitignore">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="168">
<caret line="8" selection-start-line="8" selection-end-line="8" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/index.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="2667">
<caret line="127" column="32" lean-forward="true" selection-start-line="127" selection-start-column="32" selection-end-line="127" selection-end-column="32" />
<folding>
<element signature="e#0#15#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/templates/user.html">
<provider selected="true" editor-type-id="text-editor">
<state>
<caret selection-end-line="20" selection-end-column="4" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/templates/base.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="194">
<caret line="18" selection-start-line="18" selection-end-line="18" selection-end-column="19" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/templates/stat.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="509">
<caret line="78" column="14" selection-start-line="78" selection-start-column="14" selection-end-line="78" selection-end-column="14" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/templates/index.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="488">
<caret line="77" column="14" selection-start-line="77" selection-start-column="14" selection-end-line="77" selection-end-column="14" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/templates/conf.html">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="467">
<caret line="59" column="14" lean-forward="true" selection-start-line="59" selection-start-column="14" selection-end-line="59" selection-end-column="14" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/settings.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="126">
<caret line="6" column="17" selection-start-line="6" selection-start-column="17" selection-end-line="6" selection-end-column="17" />
</state>
</provider>
</entry>
</component>
</project>

View File

@ -1,7 +1,9 @@
import logging
import sqlite3
from datetime import datetime
from dateutil import parser
import logging
class DataBase:
def __init__(self, basefile):
@ -12,7 +14,6 @@ class DataBase:
check_same_thread=False)
except:
self.log.info('Could not connect to DataBase.')
return None
def execute(self, sql):
cursor = self.conn.cursor()
@ -25,50 +26,53 @@ class DataBase:
VALUES ('%s')" % word
self.execute(sql)
sql = "SELECT id FROM word WHERE word = '%s'" % word
return(self.execute(sql)[0][0])
return self.execute(sql)[0][0]
'''
def get_memes(self, offset=0):
sql = "SELECT * FROM `meme` ORDER BY rowid DESC "
return(self.execute(sql))
return self.execute(sql)
'''
def get_user_count(self):
sql = "SELECT count(*) FROM `user`"
return(self.execute(sql)[0])
return self.execute(sql)[0]
def get_word_count(self):
sql = "SELECT count(*) FROM `word`"
return(self.execute(sql)[0])
return self.execute(sql)[0]
def get_relations_count(self):
sql = "SELECT count(*) FROM `relations`"
return(self.execute(sql)[0])
return self.execute(sql)[0]
def get_confs_count(self):
sql = "SELECT count(*) FROM `conf`"
return(self.execute(sql)[0])
return self.execute(sql)[0]
def get_users(self, order='id', sorting='ASC'):
# sql injection prevention
if sorting == 'ASC':
sorting = 'ASC'
pass
else:
sorting = 'DESC'
if order == 'id':
order = 'id'
pass
elif order == 'first_name':
order = 'first_name'
pass
elif order == 'last_name':
order == 'last_name'
pass
elif order == 'username':
order = 'username'
pass
elif order == 'firstly_seen':
order = 'dt'
pass
elif order == 'last_activity':
order = 'last_seen'
pass
elif order == 'count':
order = 'count'
pass
else:
order = 'id'
sql = """
SELECT * FROM (
SELECT u.id,
@ -84,7 +88,7 @@ class DataBase:
)
ORDER BY %s %s""" % (
order, sorting)
return(self.execute(sql))
return self.execute(sql)
def get_confs(self):
sql = """
@ -110,7 +114,7 @@ class DataBase:
ON t1.id = c.id
GROUP BY c.id
"""
return(self.execute(sql))
return self.execute(sql)
def get_user_info(self, user_id):
if not user_id.isdigit():

View File

@ -1,4 +1,7 @@
import datetime
import logging
import requests
from flask import Flask, request, send_from_directory
from flask import render_template
@ -14,14 +17,16 @@ except:
log.warning("You should have to create settings.py \
file. Look into settings.py-example")
import sys
sys.exit(1)
app = Flask(__name__, static_url_path='')
db = DataBase(db_location)
# serve memes
#@app.route('/meme/<path:path>')
#def send_meme(path):
# @app.route('/meme/<path:path>')
# def send_meme(path):
# return send_from_directory('meme', path)
# serve static
@ -29,10 +34,11 @@ db = DataBase(db_location)
def serve_static(path):
return send_from_directory('static', path)
@app.route('/')
def index():
order = request.args.get('order', default = 'id', type = str)
sorting = request.args.get('sorting', default = 'ASC', type = str)
order = request.args.get('order', default='id', type=str)
sorting = request.args.get('sorting', default='ASC', type=str)
totals = {
'users': db.get_user_count(),
'words': db.get_word_count(),
@ -44,7 +50,8 @@ def index():
users=db.get_users(order=order, sorting=sorting),
sorting=sorting,
totals=totals
)
)
@app.route('/conf')
def conf():
@ -60,6 +67,7 @@ def conf():
confs=db.get_confs(),
totals=totals)
@app.route('/overview/user/<user_id>')
def user_overview(user_id):
totals = {
@ -75,8 +83,65 @@ def user_overview(user_id):
totals=totals)
def get_threads(board):
return requests.get(url=f'https://2ch.hk/{board}/threads.json').json()
def sort_threads(threads, order, sorting):
is_asc = True if sorting == 'ASC' else False
if order == 'views':
sorted_threads = sorted(threads['threads'], key=lambda thread: (
thread['views'], thread['score']), reverse=is_asc)
elif order == 'score':
sorted_threads = sorted(threads['threads'], key=lambda thread: (
thread['score'], thread['views']), reverse=is_asc)
elif order == 'posts':
sorted_threads = sorted(threads['threads'], key=lambda thread: (
thread['posts_count'], thread['views']), reverse=is_asc)
elif order == 'timestamp':
sorted_threads = sorted(threads['threads'], key=lambda thread: (
thread['timestamp'], thread['views']), reverse=is_asc)
elif order == 'lasthit':
sorted_threads = sorted(threads['threads'], key=lambda thread: (
thread['lasthit'], thread['views']), reverse=is_asc)
else:
sorted_threads = threads['threads']
return sorted_threads
@app.template_filter('time')
def to_time(s):
return datetime.datetime.fromtimestamp(s).strftime('%H:%M:%S %d.%m.%Y ')
@app.route('/stat', methods=['GET', 'POST'])
def stat():
board = request.args.get('board', default='b', type=str)
order = request.args.get('order', default='id', type=str)
sorting = request.args.get('sorting', default='ASC', type=str)
if board not in boards_list:
board = 'b'
threads = get_threads(board)
sorted_threads = sort_threads(threads, order, sorting)
posts = sum(thread['posts_count'] for thread in sorted_threads)
return render_template(
'stat.html',
board=threads['board'],
sorting=sorting,
posts=posts,
threads=sorted_threads,
)
def main():
app.run(host=flask_host, port=flask_port)
if __name__ == '__main__':
main()

View File

@ -1,8 +1,19 @@
# database confs
db_location = './main.db'
#db_scheme = './misc/pypadla.db.sql'
# db_scheme = './misc/pypadla.db.sql'
# flask config
flask_host = '0.0.0.0'
flask_port = 8080
flask_host = '0.0.0.0'
flask_port = 8080
boards_list = ['fag', 'fg', 'fur', 'gg', 'ga', 'h', 'ho', 'sex', 'fet', 'e', 'hc', 'guro', 'vape', 'bg', 'cg',
'mmo', 'tes', 'vg', 'wr', 'moba', 'v', 'pok', 'ruvn', 'po', 'news', 'ew', 'hh', 'pvc', 'ph', 'tr',
'dom', 'izd', 'td', 'mc', 'aa', 'rm', 'to', 'web', 'br', 'trv', 'gb', 'fs', 'cul', 'out', 'old',
'cc', 'ussr', 'jsf', 'ukr', 'sw', 'law', 'm', 'ya', 'r34', 'qtr4', 'wow', 'gabe', 'cute', 'by', 'se',
'kz', '8', 'es', 'alco', 'brg', 'mlpr', 'ro', 'who', 'srv', 'asmr', 'dr', 'electrach', 'ing', 'got',
'crypt', 'socionics', 'lap', 'smo', 'hg', 'sad', 'fi', 'nvr', 'ind', 'ld', 'fem', 'gsg', 'kpop',
'vr', 'arg', 'char', 'obr', 'hv', '2d', 'wwe', 'ch', 'int', 'math', 'b', 'd', 'soc', 'r', 'abu',
'media', 'di', 'de', 'diy', 'mus', 'pa', 'p', 'wp', 'wrk', 'bi', 'biz', 'bo', 'c', 'em', 'fa', 'fiz',
'fl', 'ftb', 'hi', 'me', 'mg', 'mlp', 'mo', 'ne', 'psy', 're', 'sf', 'sci', 'sn', 'sp', 'spc', 'tv',
'un', 'w', 'wh', 'wm', 'mov', 'rf', 'mu', 'au', 'zog', 'o', 'hw', 'pr', 'ra', 's', 't', 'gd', 'mobi',
'a', 'fd', 'ja', 'ma', 'vn']

BIN
static/css/.DS_Store vendored

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,331 +0,0 @@
/*!
* Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)
* Copyright 2011-2018 The Bootstrap Authors
* Copyright 2011-2018 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
font-family: sans-serif;
line-height: 1.15;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
-ms-overflow-style: scrollbar;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
@-ms-viewport {
width: device-width;
}
article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
display: block;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
text-align: left;
background-color: #fff;
}
[tabindex="-1"]:focus {
outline: 0 !important;
}
hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 0.5rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title],
abbr[data-original-title] {
text-decoration: underline;
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
border-bottom: 0;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: .5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
dfn {
font-style: italic;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 80%;
}
sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -.25em;
}
sup {
top: -.5em;
}
a {
color: #007bff;
text-decoration: none;
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
a:hover {
color: #0056b3;
text-decoration: underline;
}
a:not([href]):not([tabindex]) {
color: inherit;
text-decoration: none;
}
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
color: inherit;
text-decoration: none;
}
a:not([href]):not([tabindex]):focus {
outline: 0;
}
pre,
code,
kbd,
samp {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 1em;
}
pre {
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
-ms-overflow-style: scrollbar;
}
figure {
margin: 0 0 1rem;
}
img {
vertical-align: middle;
border-style: none;
}
svg {
overflow: hidden;
vertical-align: middle;
}
table {
border-collapse: collapse;
}
caption {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
color: #6c757d;
text-align: left;
caption-side: bottom;
}
th {
text-align: inherit;
}
label {
display: inline-block;
margin-bottom: 0.5rem;
}
button {
border-radius: 0;
}
button:focus {
outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
input {
overflow: visible;
}
button,
select {
text-transform: none;
}
button,
html [type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
padding: 0;
border-style: none;
}
input[type="radio"],
input[type="checkbox"] {
box-sizing: border-box;
padding: 0;
}
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"] {
-webkit-appearance: listbox;
}
textarea {
overflow: auto;
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
display: block;
width: 100%;
max-width: 100%;
padding: 0;
margin-bottom: .5rem;
font-size: 1.5rem;
line-height: inherit;
color: inherit;
white-space: normal;
}
progress {
vertical-align: baseline;
}
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
[type="search"] {
outline-offset: -2px;
-webkit-appearance: none;
}
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
summary {
display: list-item;
cursor: pointer;
}
template {
display: none;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

File diff suppressed because one or more lines are too long

View File

@ -1,8 +0,0 @@
/*!
* Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)
* Copyright 2011-2018 The Bootstrap Authors
* Copyright 2011-2018 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.min.css.map */

File diff suppressed because one or more lines are too long

9030
static/css/bootstrap.css vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

3944
static/js/bootstrap.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

31
templates/base.html Normal file
View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO"
crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="../static/css/style.css">
<title>libOpenAnal appliance</title>
{% endblock %}
</head>
<body>
<div class="container" id="content">{% block content %}
<h1>Hexor's conf_bot data extractor tool</h1>{% endblock %}</div>
{% block scripts %}
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"
integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"
integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy"
crossorigin="anonymous"></script>
{% endblock %}
</body>
</html>

View File

@ -1,83 +1,60 @@
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO"
crossorigin="anonymous">
<title>libOpenAnal appliance</title>
</head>
<body>
<div class="container">
<h1>Hexor's conf_bot data extractor tool</h1>
<ul class="nav">
<li class="nav-item">
<a class="nav-link active" href="/">Users</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/conf">Conferences</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#">Disabled</a>
</li>
{% extends "base.html" %}
{% block head %}
{{ super() }}
{% endblock %}
{% block content %}
{{ super() }}
<ul class="nav nav-pills">
<li class="nav-item">
<a class="nav-link" href="/">Users</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/conf">Conferences</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/stat">2ch.hk Stats</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#">Disabled</a>
</li>
</ul>
<br>
<div class="row">
<div class="col-sm-12">
<h4>Totals</h4>
<b>Users: </b> {{ totals.users[0] }}<br>
<b>Unique words: </b> {{ totals.words[0] }}<br>
<b>Words said: </b> {{ totals.relations[0] }}<br>
<b>Chats fetched: </b> {{ totals.confs[0] }}<br>
</div>
<div class="col-sm-12">
<h4>Totals</h4>
<b>Users: </b> {{ totals.users[0] }}<br>
<b>Unique words: </b> {{ totals.words[0] }}<br>
<b>Words said: </b> {{ totals.relations[0] }}<br>
<b>Chats fetched: </b> {{ totals.confs[0] }}<br>
</div>
</div>
<table class="table table-hover table-sm">
<thead>
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Title</th>
<th scope="col">ID</th>
<th scope="col">Added</th>
<th scope="col">Members</th>
<th scope="col">Words said</th>
<th scope="col">#</th>
<th scope="col">Title</th>
<th scope="col">ID</th>
<th scope="col">Added</th>
<th scope="col">Members</th>
<th scope="col">Words said</th>
</tr>
</thead>
<tbody>
</thead>
<tbody>
{% for conf in confs %}
<tr>
<th scope="row">{{loop.index}}</th>
<td>{{ conf.0 }}</td>
<td>{{ conf.1 }}</td>
<td>{{ conf.2 }}</td>
<td>{{ conf.4 }}</td>
<td>{{ conf.3 }}</td>
</tr>
<tr>
<th scope="row">{{ loop.index }}</th>
<td>{{ conf.0 }}</td>
<td>{{ conf.1 }}</td>
<td>{{ conf.2 }}</td>
<td>{{ conf.4 }}</td>
<td>{{ conf.3 }}</td>
</tr>
{% endfor %}
</tbody>
</tbody>
</table>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy"
crossorigin="anonymous"></script>
</div>
</div>
</div>
</body>
</html>
{% endblock %}
{% block scripts %}
{{ super() }}
{% endblock %}

View File

@ -1,91 +1,78 @@
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO"
crossorigin="anonymous">
<title>libOpenAnal appliance</title>
</head>
<body>
<div class="container">
<h1>Hexor's conf_bot data extractor tool</h1>
<ul class="nav">
<li class="nav-item">
<a class="nav-link active" href="/">Users</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/conf">Conferences</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#">Disabled</a>
</li>
{% extends "base.html" %}
{% block head %}
{{ super() }}
{% endblock %}
{% block content %}
{{ super() }}
<ul class="nav nav-pills">
<li class="nav-item">
<a class="nav-link active" href="/">Users</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/conf">Conferences</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/stat">2ch.hk Stats</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#">Disabled</a>
</li>
</ul>
<br>
<div class="row">
<div class="col-sm-12">
<h4>Totals</h4>
<b>Users: </b> {{ totals.users[0] }}<br>
<b>Unique words: </b> {{ totals.words[0] }}<br>
<b>Words said: </b> {{ totals.relations[0] }}<br>
<b>Chats fetched: </b> {{ totals.confs[0] }}<br>
</div>
<div class="col-sm-12">
<h4>Totals</h4>
<b>Users: </b> {{ totals.users[0] }}<br>
<b>Unique words: </b> {{ totals.words[0] }}<br>
<b>Words said: </b> {{ totals.relations[0] }}<br>
<b>Chats fetched: </b> {{ totals.confs[0] }}<br>
</div>
</div>
<br>
<table class="table table-hover table-sm">
<thead>
<thead>
<tr>
<th scope="col">#</th>
<th scope="col"><a href="./?order=first_name&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">First
name</a></th>
<th scope="col"><a href="./?order=last_name&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Last
name</a></th>
<th scope="col"><a href="./?order=username&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Username</a></th>
<th scope="col"><a href="./?order=id&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">User
ID</a></th>
<th scope="col"><a href="./?order=firstly_seen&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Firstly
seen</a></th>
<th scope="col"><a href="./?order=last_activity&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Last
activity</a></th>
<th scope="col"><a href="./?order=count&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Word
count</a></th>
<th scope="col">#</th>
<th scope="col"><a
href="./?order=first_name&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">First
name</a></th>
<th scope="col"><a
href="./?order=last_name&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Last
name</a></th>
<th scope="col"><a
href="./?order=username&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Username</a>
</th>
<th scope="col"><a href="./?order=id&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">User
ID</a></th>
<th scope="col"><a
href="./?order=firstly_seen&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Firstly
seen</a></th>
<th scope="col"><a
href="./?order=last_activity&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Last
activity</a></th>
<th scope="col"><a
href="./?order=count&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Word
count</a></th>
</tr>
</thead>
<tbody>
</thead>
<tbody>
{% for user in users %}
<tr>
<th scope="row">{{loop.index}}</th>
<td><a href="/overview/user/{{ user.0 }}">{{ user.2 }}</a></td>
<td>{% if user.3 != '_null' %}{{ user.3 }}{% else %}<span class="badge badge-warning">N/D</span>{% endif %}</td>
<td>{% if user.1 != '_null' %}{{ user.1 }}{% else %}<span class="badge badge-warning">N/D</span>{% endif %}</td>
<td><a class="badge badge-dark " href="/overview/user/{{ user.0 }}">{{ user.0 }}</a></td>
<td>{{ user.4 }}</td>
<td>{{ user.5 }}</td>
<td>{{ user.6 }}</td>
</tr>
<tr>
<th scope="row">{{ loop.index }}</th>
<td><a href="/overview/user/{{ user.0 }}">{{ user.2 }}</a></td>
<td>{% if user.3 != '_null' %}{{ user.3 }}{% else %}
<span class="badge badge-warning">N/D</span>{% endif %}</td>
<td>{% if user.1 != '_null' %}{{ user.1 }}{% else %}
<span class="badge badge-warning">N/D</span>{% endif %}</td>
<td><a class="badge badge-dark " href="/overview/user/{{ user.0 }}">{{ user.0 }}</a></td>
<td>{{ user.4 }}</td>
<td>{{ user.5 }}</td>
<td>{{ user.6 }}</td>
</tr>
{% endfor %}
</tbody>
</tbody>
</table>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy"
crossorigin="anonymous"></script>
</div>
</div>
</div>
</body>
</html>
{% endblock %}
{% block scripts %}
{{ super() }}
{% endblock %}

79
templates/stat.html Normal file
View File

@ -0,0 +1,79 @@
{% extends "base.html" %}
{% block head %}
{{ super() }}
{% endblock %}
{% block content %}
{{ super() }}
<ul class="nav nav-pills">
<li class="nav-item">
<a class="nav-link" href="/">Users</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/conf">Conferences</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/stat">2ch.hk Stats</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#">Disabled</a>
</li>
</ul>
<br>
<form action="/" method="get">
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="Board" aria-label="Board"
aria-describedby="button-stat" name="board">
<div class="input-group-append">
<button class="btn btn-primary" type="submit" id="button-stat">Get stat</button>
</div>
</div>
</form>
<div class="row">
<div class="col-sm-12">
<h4>Info</h4>
<b>Board: </b> {{ board }}<br>
<b>Threads: </b> {{ threads|length }}<br>
<b>Total posts: </b> {{ posts }}<br>
</div>
</div>
<br>
<table class="table table-hover table-sm">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Subject</th>
<th scope="col"><a
href="./stat?board={{ board }}&order=posts&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Posts</a>
</th>
<th scope="col"><a
href="./stat?board={{ board }}&order=views&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Views</a>
</th>
<th scope="col"><a
href="./stat?board={{ board }}&order=score&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Score</a>
</th>
<th scope="col"><a
href="./stat?board={{ board }}&order=timestamp&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Created</a>
</th>
<th scope="col"><a
href="./stat?board={{ board }}&order=lasthit&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Lasthit</a>
</th>
</tr>
</thead>
<tbody>
{% for thread in threads %}
<tr>
<th scope="row">{{ loop.index }}</th>
<td><a href="https://2ch.hk/b/res/{{ thread['num'] }}.html">{{ thread['subject'] }}</a></td>
<td>{{ thread['posts_count'] }}</td>
<td>{{ thread['views'] }}</td>
<td>{{ "%.2f"|format(thread['score']|float) }}</td>
<td>{{ thread['timestamp'] | time }}</td>
<td>{{ thread['lasthit'] | time }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
{% block scripts %}
{{ super() }}
{% endblock %}

View File

@ -1,140 +1,114 @@
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO"
crossorigin="anonymous">
<title>libOpenAnal appliance</title>
</head>
<body>
<div class="container">
<h1>Hexor's conf_bot data extractor tool</h1>
<ul class="nav">
<li class="nav-item">
<a class="nav-link active" href="/">Users</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/conf">Conferences</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#">Disabled</a>
</li>
</ul>
<hr>
<h4>{{ user_info.first_name }} user's data</h4>
<hr>
<div class="card-columns">
<div class="card">
<div class="card-body">
<h5 class="card-title">Identity</h5>
<hr>
<p class="card-text">
<b>First name: </b>{{ user_info.first_name }}<br>
<b>Last name: </b>{% if user_info.last_name != '_null' %}{{ user_info.last_name }}{% else %}<span class="badge badge-warning">N/D</span>{% endif %}<br>
<b>Username: </b>{% if user_info.username != '_null' %}{{ user_info.username }}{% else %}<span class="badge badge-warning">N/D</span>{% endif %}<br>
<b>Telegram ID: </b>{{user_info.id }}<br>
<b>Firstly seen: </b>{{user_info.first_date }}<br>
</p>
</div>
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title">Activity</h5>
<hr>
<p class="card-text">
<b>First message: </b>{{user_info.first_date }}<br>
<b>Last message: </b>{{user_info.last_message }}<br>
<b>Days known: </b>{{user_info.day_known }}<br>
<b>Word said: </b>{{ user_info.word_count }}<br>
<b>Words per day: </b>{{'%0.2f'| format((user_info.word_count / user_info.day_known)|float)}}<br>
<b>Words per message: </b>~{{'%0.2f'| format(user_info.avg|float)}}<br>
</p>
</div>
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title">Top</h5>
<hr>
<p class="card-text">
<table class="table table-hover table-sm">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Word</th>
<th scope="col">Said</th>
</tr>
</thead>
<tbody>
{% for word in user_info.top %}
<tr>
<th scope="row">{{loop.index}}</th>
<td>{{word[0]}}</td>
{% if not loop.last %}
{% if (word[1]/loop.nextitem[1]) > 2 %}
<td><span class="badge badge-danger" data-toggle="tooltip" data-placement="right" title="Must have been abused">{{word[1]}} </span></td>
{% else %}
<td><span class="badge badge-secondary">{{word[1]}} </span></td>
{% endif %}
{% else %}
<td><span class="badge badge-secondary">{{word[1]}} </span></td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
</p>
</div>
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title">Chats</h5>
<p class="card-text">
<table class="table table-hover table-sm">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Chat</th>
<th scope="col">Words said</th>
</tr>
</thead>
<tbody>
{% for chat in user_info.chats %}
<tr>
<th scope="row">{{loop.index}}</th>
<td>{{chat[0]}}</td>
<td><span class="badge badge-secondary">{{chat[1]}} </span></td>
</tr>
{% endfor %}
</tbody>
</table>
</p>
</div>
</div>
{% extends "base.html" %}
{% block head %}
{{ super() }}
{% endblock %}
{% block content %}
{{ super() }}
<ul class="nav nav-pills">
<li class="nav-item">
<a class="nav-link active" href="/">Users</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/conf">Conferences</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/stat">2ch.hk Stats</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#">Disabled</a>
</li>
</ul>
<br>
<hr>
<h4>{{ user_info.first_name }} user's data</h4>
<hr>
<div class="card-columns">
<div class="card">
<div class="card-body">
<h5 class="card-title">Identity</h5>
<hr>
<p class="card-text">
<b>First name: </b>{{ user_info.first_name }}<br>
<b>Last name: </b>{% if user_info.last_name != '_null' %}{{ user_info.last_name }}{% else %}
<span class="badge badge-warning">N/D</span>{% endif %}<br>
<b>Username: </b>{% if user_info.username != '_null' %}{{ user_info.username }}{% else %}
<span class="badge badge-warning">N/D</span>{% endif %}<br>
<b>Telegram ID: </b>{{ user_info.id }}<br>
<b>Firstly seen: </b>{{ user_info.first_date }}<br>
</p>
</div>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy"
crossorigin="anonymous"></script>
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title">Activity</h5>
<hr>
<p class="card-text">
<b>First message: </b>{{ user_info.first_date }}<br>
<b>Last message: </b>{{ user_info.last_message }}<br>
<b>Days known: </b>{{ user_info.day_known }}<br>
<b>Word said: </b>{{ user_info.word_count }}<br>
<b>Words per day: </b>{{ '%0.2f'| format((user_info.word_count / user_info.day_known)|float) }}<br>
<b>Words per message: </b>~{{ '%0.2f'| format(user_info.avg|float) }}<br>
</p>
</div>
</div>
<div class="card">
<div class="card-body">
<h5 class="card-title">Top</h5>
<hr>
<p class="card-text">
<table class="table table-hover table-sm">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Word</th>
<th scope="col">Said</th>
</tr>
</thead>
<tbody>
{% for word in user_info.top %}
<tr>
<th scope="row">{{ loop.index }}</th>
<td>{{ word[0] }}</td>
{% if not loop.last %}
{% if (word[1]/loop.nextitem[1]) > 2 %}
<td><span class="badge badge-danger" data-toggle="tooltip" data-placement="right"
title="Must have been abused">{{ word[1] }} </span></td>
{% else %}
<td><span class="badge badge-secondary">{{ word[1] }} </span></td>
{% endif %}
{% else %}
<td><span class="badge badge-secondary">{{ word[1] }} </span></td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</body>
</html>
<div class="card">
<div class="card-body">
<h5 class="card-title">Chats</h5>
<p class="card-text">
<table class="table table-hover table-sm">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Chat</th>
<th scope="col">Words said</th>
</tr>
</thead>
<tbody>
{% for chat in user_info.chats %}
<tr>
<th scope="row">{{ loop.index }}</th>
<td>{{ chat[0] }}</td>
<td><span class="badge badge-secondary">{{ chat[1] }} </span></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}