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

View File

@ -1,4 +1,7 @@
import datetime
import logging import logging
import requests
from flask import Flask, request, send_from_directory from flask import Flask, request, send_from_directory
from flask import render_template from flask import render_template
@ -14,14 +17,16 @@ except:
log.warning("You should have to create settings.py \ log.warning("You should have to create settings.py \
file. Look into settings.py-example") file. Look into settings.py-example")
import sys import sys
sys.exit(1) sys.exit(1)
app = Flask(__name__, static_url_path='') app = Flask(__name__, static_url_path='')
db = DataBase(db_location) db = DataBase(db_location)
# serve memes # serve memes
#@app.route('/meme/<path:path>') # @app.route('/meme/<path:path>')
#def send_meme(path): # def send_meme(path):
# return send_from_directory('meme', path) # return send_from_directory('meme', path)
# serve static # serve static
@ -29,10 +34,11 @@ db = DataBase(db_location)
def serve_static(path): def serve_static(path):
return send_from_directory('static', path) return send_from_directory('static', path)
@app.route('/') @app.route('/')
def index(): def index():
order = request.args.get('order', default = 'id', type = str) order = request.args.get('order', default='id', type=str)
sorting = request.args.get('sorting', default = 'ASC', type = str) sorting = request.args.get('sorting', default='ASC', type=str)
totals = { totals = {
'users': db.get_user_count(), 'users': db.get_user_count(),
'words': db.get_word_count(), 'words': db.get_word_count(),
@ -46,6 +52,7 @@ def index():
totals=totals totals=totals
) )
@app.route('/conf') @app.route('/conf')
def conf(): def conf():
totals = { totals = {
@ -60,6 +67,7 @@ def conf():
confs=db.get_confs(), confs=db.get_confs(),
totals=totals) totals=totals)
@app.route('/overview/user/<user_id>') @app.route('/overview/user/<user_id>')
def user_overview(user_id): def user_overview(user_id):
totals = { totals = {
@ -75,8 +83,65 @@ def user_overview(user_id):
totals=totals) 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(): def main():
app.run(host=flask_host, port=flask_port) app.run(host=flask_host, port=flask_port)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@ -1,8 +1,19 @@
# database confs # database confs
db_location = './main.db' db_location = './main.db'
#db_scheme = './misc/pypadla.db.sql' # db_scheme = './misc/pypadla.db.sql'
# flask config # flask config
flask_host = '0.0.0.0' flask_host = '0.0.0.0'
flask_port = 8080 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,37 +1,24 @@
<!doctype html> {% extends "base.html" %}
<html lang="en"> {% block head %}
{{ super() }}
<head> {% endblock %}
<!-- Required meta tags --> {% block content %}
<meta charset="utf-8"> {{ super() }}
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <ul class="nav nav-pills">
<!-- 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"> <li class="nav-item">
<a class="nav-link active" href="/">Users</a> <a class="nav-link" href="/">Users</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/conf">Conferences</a> <a class="nav-link active" href="/conf">Conferences</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="#">Link</a> <a class="nav-link" href="/stat">2ch.hk Stats</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link disabled" href="#">Disabled</a> <a class="nav-link disabled" href="#">Disabled</a>
</li> </li>
</ul> </ul>
<br>
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
@ -57,7 +44,7 @@
<tbody> <tbody>
{% for conf in confs %} {% for conf in confs %}
<tr> <tr>
<th scope="row">{{loop.index}}</th> <th scope="row">{{ loop.index }}</th>
<td>{{ conf.0 }}</td> <td>{{ conf.0 }}</td>
<td>{{ conf.1 }}</td> <td>{{ conf.1 }}</td>
<td>{{ conf.2 }}</td> <td>{{ conf.2 }}</td>
@ -67,17 +54,7 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
<!-- Optional JavaScript --> {% endblock %}
<!-- jQuery first, then Popper.js, then Bootstrap JS --> {% block scripts %}
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" {{ super() }}
crossorigin="anonymous"></script> {% endblock %}
<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>

View File

@ -1,23 +1,10 @@
<!doctype html> {% extends "base.html" %}
<html lang="en"> {% block head %}
{{ super() }}
<head> {% endblock %}
<!-- Required meta tags --> {% block content %}
<meta charset="utf-8"> {{ super() }}
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <ul class="nav nav-pills">
<!-- 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"> <li class="nav-item">
<a class="nav-link active" href="/">Users</a> <a class="nav-link active" href="/">Users</a>
</li> </li>
@ -25,12 +12,13 @@
<a class="nav-link" href="/conf">Conferences</a> <a class="nav-link" href="/conf">Conferences</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="#">Link</a> <a class="nav-link" href="/stat">2ch.hk Stats</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link disabled" href="#">Disabled</a> <a class="nav-link disabled" href="#">Disabled</a>
</li> </li>
</ul> </ul>
<br>
<div class="row"> <div class="row">
<div class="col-sm-12"> <div class="col-sm-12">
<h4>Totals</h4> <h4>Totals</h4>
@ -40,33 +28,42 @@
<b>Chats fetched: </b> {{ totals.confs[0] }}<br> <b>Chats fetched: </b> {{ totals.confs[0] }}<br>
</div> </div>
</div> </div>
<br>
<table class="table table-hover table-sm"> <table class="table table-hover table-sm">
<thead> <thead>
<tr> <tr>
<th scope="col">#</th> <th scope="col">#</th>
<th scope="col"><a href="./?order=first_name&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">First <th scope="col"><a
href="./?order=first_name&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">First
name</a></th> name</a></th>
<th scope="col"><a href="./?order=last_name&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Last <th scope="col"><a
href="./?order=last_name&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Last
name</a></th> 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=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 <th scope="col"><a href="./?order=id&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">User
ID</a></th> ID</a></th>
<th scope="col"><a href="./?order=firstly_seen&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Firstly <th scope="col"><a
href="./?order=firstly_seen&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Firstly
seen</a></th> seen</a></th>
<th scope="col"><a href="./?order=last_activity&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Last <th scope="col"><a
href="./?order=last_activity&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Last
activity</a></th> activity</a></th>
<th scope="col"><a href="./?order=count&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Word <th scope="col"><a
href="./?order=count&sorting={%- if sorting == 'ASC' -%}DESC{%- else -%}ASC{%- endif -%}">Word
count</a></th> count</a></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for user in users %} {% for user in users %}
<tr> <tr>
<th scope="row">{{loop.index}}</th> <th scope="row">{{ loop.index }}</th>
<td><a href="/overview/user/{{ user.0 }}">{{ user.2 }}</a></td> <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.3 != '_null' %}{{ user.3 }}{% else %}
<td>{% if user.1 != '_null' %}{{ user.1 }}{% else %}<span class="badge badge-warning">N/D</span>{% endif %}</td> <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><a class="badge badge-dark " href="/overview/user/{{ user.0 }}">{{ user.0 }}</a></td>
<td>{{ user.4 }}</td> <td>{{ user.4 }}</td>
<td>{{ user.5 }}</td> <td>{{ user.5 }}</td>
@ -75,17 +72,7 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
<!-- Optional JavaScript --> {% endblock %}
<!-- jQuery first, then Popper.js, then Bootstrap JS --> {% block scripts %}
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" {{ super() }}
crossorigin="anonymous"></script> {% endblock %}
<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>

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,23 +1,10 @@
<!doctype html> {% extends "base.html" %}
<html lang="en"> {% block head %}
{{ super() }}
<head> {% endblock %}
<!-- Required meta tags --> {% block content %}
<meta charset="utf-8"> {{ super() }}
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <ul class="nav nav-pills">
<!-- 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"> <li class="nav-item">
<a class="nav-link active" href="/">Users</a> <a class="nav-link active" href="/">Users</a>
</li> </li>
@ -25,26 +12,29 @@
<a class="nav-link" href="/conf">Conferences</a> <a class="nav-link" href="/conf">Conferences</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="#">Link</a> <a class="nav-link" href="/stat">2ch.hk Stats</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link disabled" href="#">Disabled</a> <a class="nav-link disabled" href="#">Disabled</a>
</li> </li>
</ul> </ul>
<hr> <br>
<h4>{{ user_info.first_name }} user's data</h4> <hr>
<hr> <h4>{{ user_info.first_name }} user's data</h4>
<div class="card-columns"> <hr>
<div class="card-columns">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">Identity</h5> <h5 class="card-title">Identity</h5>
<hr> <hr>
<p class="card-text"> <p class="card-text">
<b>First name: </b>{{ user_info.first_name }}<br> <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>Last name: </b>{% if user_info.last_name != '_null' %}{{ user_info.last_name }}{% else %}
<b>Username: </b>{% if user_info.username != '_null' %}{{ user_info.username }}{% else %}<span class="badge badge-warning">N/D</span>{% endif %}<br> <span class="badge badge-warning">N/D</span>{% endif %}<br>
<b>Telegram ID: </b>{{user_info.id }}<br> <b>Username: </b>{% if user_info.username != '_null' %}{{ user_info.username }}{% else %}
<b>Firstly seen: </b>{{user_info.first_date }}<br> <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> </p>
</div> </div>
</div> </div>
@ -53,12 +43,12 @@
<h5 class="card-title">Activity</h5> <h5 class="card-title">Activity</h5>
<hr> <hr>
<p class="card-text"> <p class="card-text">
<b>First message: </b>{{user_info.first_date }}<br> <b>First message: </b>{{ user_info.first_date }}<br>
<b>Last message: </b>{{user_info.last_message }}<br> <b>Last message: </b>{{ user_info.last_message }}<br>
<b>Days known: </b>{{user_info.day_known }}<br> <b>Days known: </b>{{ user_info.day_known }}<br>
<b>Word said: </b>{{ user_info.word_count }}<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 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> <b>Words per message: </b>~{{ '%0.2f'| format(user_info.avg|float) }}<br>
</p> </p>
</div> </div>
</div> </div>
@ -78,22 +68,22 @@
<tbody> <tbody>
{% for word in user_info.top %} {% for word in user_info.top %}
<tr> <tr>
<th scope="row">{{loop.index}}</th> <th scope="row">{{ loop.index }}</th>
<td>{{word[0]}}</td> <td>{{ word[0] }}</td>
{% if not loop.last %} {% if not loop.last %}
{% if (word[1]/loop.nextitem[1]) > 2 %} {% 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> <td><span class="badge badge-danger" data-toggle="tooltip" data-placement="right"
title="Must have been abused">{{ word[1] }} </span></td>
{% else %} {% else %}
<td><span class="badge badge-secondary">{{word[1]}} </span></td> <td><span class="badge badge-secondary">{{ word[1] }} </span></td>
{% endif %} {% endif %}
{% else %} {% else %}
<td><span class="badge badge-secondary">{{word[1]}} </span></td> <td><span class="badge badge-secondary">{{ word[1] }} </span></td>
{% endif %} {% endif %}
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</p>
</div> </div>
</div> </div>
<div class="card"> <div class="card">
@ -111,30 +101,14 @@
<tbody> <tbody>
{% for chat in user_info.chats %} {% for chat in user_info.chats %}
<tr> <tr>
<th scope="row">{{loop.index}}</th> <th scope="row">{{ loop.index }}</th>
<td>{{chat[0]}}</td> <td>{{ chat[0] }}</td>
<td><span class="badge badge-secondary">{{chat[1]}} </span></td> <td><span class="badge badge-secondary">{{ chat[1] }} </span></td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</p>
</div> </div>
</div> </div>
</div> </div>
{% endblock %}
<!-- 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>