├── .dockerignore ├── .gitignore ├── Dockerfile ├── Dockerfile.gpu ├── LICENSE ├── README.md ├── chatbot ├── __init__.py ├── chatbot.py ├── corpus │ ├── cornelldata.py │ ├── lightweightdata.py │ ├── opensubsdata.py │ ├── scotusdata.py │ └── ubuntudata.py ├── model.py ├── textdata.py └── trainner.py ├── chatbot_miniature.png ├── chatbot_website ├── .gitignore ├── chatbot_interface │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── chatbotmanager.py │ ├── consumer.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── routing.py │ ├── static │ │ ├── LICENSE.txt │ │ ├── README.txt │ │ ├── assets │ │ │ ├── css │ │ │ │ ├── font-awesome.min.css │ │ │ │ ├── ie8.css │ │ │ │ ├── ie9.css │ │ │ │ ├── images │ │ │ │ │ └── overlay.png │ │ │ │ ├── main.css │ │ │ │ └── noscript.css │ │ │ ├── fonts │ │ │ │ ├── FontAwesome.otf │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.svg │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ └── fontawesome-webfont.woff2 │ │ │ ├── js │ │ │ │ ├── PIE.htc │ │ │ │ ├── html5shiv.js │ │ │ │ └── respond.min.js │ │ │ └── sass │ │ │ │ ├── base │ │ │ │ ├── _page.scss │ │ │ │ └── _typography.scss │ │ │ │ ├── components │ │ │ │ ├── _button.scss │ │ │ │ ├── _form.scss │ │ │ │ ├── _icon.scss │ │ │ │ └── _list.scss │ │ │ │ ├── ie8.scss │ │ │ │ ├── ie9.scss │ │ │ │ ├── layout │ │ │ │ ├── _footer.scss │ │ │ │ ├── _main.scss │ │ │ │ └── _wrapper.scss │ │ │ │ ├── libs │ │ │ │ ├── _functions.scss │ │ │ │ ├── _mixins.scss │ │ │ │ ├── _skel.scss │ │ │ │ └── _vars.scss │ │ │ │ ├── main.scss │ │ │ │ └── noscript.scss │ │ ├── images │ │ │ ├── avatar.jpg │ │ │ ├── bg.png │ │ │ └── icon.png │ │ └── js │ │ │ ├── chat.js │ │ │ ├── jquery-3.1.0.min.js │ │ │ └── reconnecting-websocket.js │ ├── templates │ │ └── index.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── chatbot_website │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── logs │ └── .gitignore └── manage.py ├── data ├── cornell │ ├── README.txt │ ├── movie_conversations.txt │ └── movie_lines.txt ├── embeddings │ ├── .gitignore │ ├── README.md │ └── vec2bin.py ├── lightweight │ ├── .gitignore │ └── README.md ├── opensubs │ ├── .gitignore │ └── README.md ├── samples │ └── .gitignore ├── scotus │ ├── .gitignore │ └── README.md ├── test │ └── samples.txt └── ubuntu │ ├── .gitignore │ └── README.md ├── docker ├── README.md ├── data_dirs.sh ├── deploy.yml └── train.yml ├── main.py ├── requirements.txt ├── save └── .gitignore ├── setup_server.sh └── testsuite.py /.dockerignore: -------------------------------------------------------------------------------- 1 | data/ 2 | save/ 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # PyCharm files 7 | .idea/ 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | env/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *,cover 49 | .hypothesis/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # IPython Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # dotenv 82 | .env 83 | 84 | # virtualenv 85 | venv/ 86 | ENV/ 87 | 88 | # Spyder project settings 89 | .spyderproject 90 | 91 | # Rope project settings 92 | .ropeproject 93 | 94 | # nvidia-docker-compose 95 | nvidia-docker-compose.yml 96 | 97 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ## Dockerfile to build DeepQ&A container image 2 | 3 | FROM python:3.5.2 4 | 5 | ## Dependencies 6 | 7 | RUN \ 8 | apt-get -qq -y update && apt-get -y install unzip 9 | 10 | RUN \ 11 | pip3 install -U nltk \ 12 | tqdm \ 13 | django \ 14 | asgi_redis \ 15 | channels && \ 16 | python3 -m nltk.downloader punkt 17 | 18 | ## Tensorflow 19 | ARG TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-1.0.0-cp35-cp35m-linux_x86_64.whl 20 | 21 | RUN \ 22 | pip3 install -U $TF_BINARY_URL 23 | 24 | COPY ./ /root/DeepQA 25 | 26 | ## Run Config 27 | 28 | # You should generate your own key if you want deploy it on a server 29 | ENV CHATBOT_SECRET_KEY="e#0y6^6mg37y9^+t^p_$xwnogcdh=27)f6_=v^$bh9p0ihd-%v" 30 | ENV CHATBOT_REDIS_URL="redis" 31 | EXPOSE 8000 32 | 33 | WORKDIR /root/DeepQA/chatbot_website 34 | RUN python3 manage.py makemigrations 35 | RUN python3 manage.py migrate 36 | 37 | # Launch the server 38 | CMD python3 manage.py runserver 0.0.0.0:8000 39 | -------------------------------------------------------------------------------- /Dockerfile.gpu: -------------------------------------------------------------------------------- 1 | ## Dockerfile to build DeepQ&A container image 2 | 3 | FROM nvidia/cuda:8.0-cudnn5-devel 4 | 5 | ## Dependencies 6 | 7 | RUN \ 8 | apt-get -qq -y update && apt-get -y install unzip python3 python3-pip 9 | 10 | RUN \ 11 | pip3 install -U nltk \ 12 | tqdm \ 13 | django \ 14 | asgi_redis \ 15 | channels && \ 16 | python3 -m nltk.downloader punkt 17 | 18 | ## Tensorflow 19 | ARG TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/gpu/tensorflow_gpu-1.0.0-cp34-cp34m-linux_x86_64.whl 20 | RUN \ 21 | pip3 install -U $TF_BINARY_URL 22 | 23 | COPY ./ /root/DeepQA 24 | 25 | ## Run Config 26 | 27 | # You should generate your own key if you want deploy it on a server 28 | ENV CHATBOT_SECRET_KEY="e#0y6^6mg37y9^+t^p_$xwnogcdh=27)f6_=v^$bh9p0ihd-%v" 29 | ENV CHATBOT_REDIS_URL="redis" 30 | EXPOSE 8000 31 | 32 | WORKDIR /root/DeepQA/chatbot_website 33 | RUN python3 manage.py makemigrations 34 | RUN python3 manage.py migrate 35 | 36 | # Launch the server 37 | CMD python3 manage.py runserver 0.0.0.0:8000 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deep Q&A 2 | [![Join the chat at https://gitter.im/chatbot-pilots/DeepQA](https://badges.gitter.im/chatbot-pilots/DeepQA.svg)](https://gitter.im/chatbot-pilots/DeepQA?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 3 | 4 | #### Table of Contents 5 | 6 | * [Presentation](#presentation) 7 | * [Installation](#installation) 8 | * [Running](#running) 9 | * [Chatbot](#chatbot) 10 | * [Web interface](#web-interface) 11 | * [Results](#results) 12 | * [Pretrained model](#pretrained-model) 13 | * [Improvements](#improvements) 14 | * [Upgrade](#upgrade) 15 | 16 | ## Presentation 17 | 18 | This work tries to reproduce the results of [A Neural Conversational Model](http://arxiv.org/abs/1506.05869) (aka the Google chatbot). It uses a RNN (seq2seq model) for sentence predictions. It is done using python and TensorFlow. 19 | 20 | The loading corpus part of the program is inspired by the Torch [neuralconvo](https://github.com/macournoyer/neuralconvo) from [macournoyer](https://github.com/macournoyer). 21 | 22 | For now, DeepQA support the following dialog corpus: 23 | * [Cornell Movie Dialogs](http://www.cs.cornell.edu/~cristian/Cornell_Movie-Dialogs_Corpus.html) corpus (default). Already included when cloning the repository. 24 | * [OpenSubtitles](http://opus.lingfil.uu.se/OpenSubtitles.php) (thanks to [Eschnou](https://github.com/eschnou)). Much bigger corpus (but also noisier). To use it, follow [those instructions](data/opensubs/) and use the flag `--corpus opensubs`. 25 | * Supreme Court Conversation Data (thanks to [julien-c](https://github.com/julien-c)). Available using `--corpus scotus`. See the [instructions](data/scotus/) for installation. 26 | * [Ubuntu Dialogue Corpus](https://arxiv.org/abs/1506.08909) (thanks to [julien-c](https://github.com/julien-c)). Available using `--corpus ubuntu`. See the [instructions](data/ubuntu/) for installation. 27 | * Your own data (thanks to [julien-c](https://github.com/julien-c)) by using a simple custom conversation format (See [here](data/lightweight) for more info). 28 | 29 | To speedup the training, it's also possible to use pre-trained word embeddings (thanks to [Eschnou](https://github.com/eschnou)). More info [here](data/embeddings). 30 | 31 | ## Installation 32 | 33 | The program requires the following dependencies (easy to install using pip: `pip3 install -r requirements.txt`): 34 | * python 3.5 35 | * tensorflow (tested with v1.0) 36 | * numpy 37 | * CUDA (for using GPU) 38 | * nltk (natural language toolkit for tokenized the sentences) 39 | * tqdm (for the nice progression bars) 40 | 41 | You might also need to download additional data to make nltk work. 42 | 43 | ``` 44 | python3 -m nltk.downloader punkt 45 | ``` 46 | 47 | The Cornell dataset is already included. For the other datasets, look at the readme files into their respective folders (inside `data/`). 48 | 49 | The web interface requires some additional packages: 50 | * django (tested with 1.10) 51 | * channels 52 | * Redis (see [here](http://redis.io/topics/quickstart)) 53 | * asgi_redis (at least 1.0) 54 | 55 | A Docker installation is also available. More detailed instructions [here](docker/README.md). 56 | 57 | ## Running 58 | 59 | ### Chatbot 60 | 61 | To train the model, simply run `main.py`. Once trained, you can test the results with `main.py --test` (results generated in 'save/model/samples_predictions.txt') or `main.py --test interactive` (more fun). 62 | 63 | Here are some flags which could be useful. For more help and options, use `python main.py -h`: 64 | * `--modelTag `: allow to give a name to the current model to differentiate between them when testing/training. 65 | * `--keepAll`: use this flag when training if when testing, you want to see the predictions at different steps (it can be interesting to see the program changes its name and age as the training progress). Warning: It can quickly take a lot of storage space if you don't increase the `--saveEvery` option. 66 | * `--filterVocab 20` or `--vocabularySize 30000`: Limit the vocabulary size to and optimize the performances and memory usage. Replace the words used less than 20 times by the `` token and set a maximum vocabulary size. 67 | * `--verbose`: when testing, will print the sentences as they are computed. 68 | * `--playDataset`: show some dialogue samples from the dataset (can be use conjointly with `--createDataset` if this is the only action you want to perform). 69 | 70 | To visualize the computational graph and the cost with [TensorBoard](https://www.tensorflow.org/how_tos/summaries_and_tensorboard/), just run `tensorboard --logdir save/`. 71 | 72 | By default, the network architecture is a standard encoder/decoder with two LSTM layers (hidden size of 256) and an embedding size for the vocabulary of 32. The network is trained using ADAM. The maximum sentence length is set to 10 words, but can be increased. 73 | 74 | ### Web interface 75 | 76 | Once trained, it's possible to chat with it using a more user friendly interface. The server will look at the model copied to `save/model-server/model.ckpt`. The first time you want to use it, you'll need to configure it with: 77 | 78 | ```bash 79 | export CHATBOT_SECRET_KEY="my-secret-key" 80 | cd chatbot_website/ 81 | python manage.py makemigrations 82 | python manage.py migrate 83 | ``` 84 | 85 | Then, to launch the server locally, use the following commands: 86 | 87 | ```bash 88 | cd chatbot_website/ 89 | redis-server & # Launch Redis in background 90 | python manage.py runserver 91 | ``` 92 | 93 | After launch, the interface should be available on [http://localhost:8000/](http://localhost:8000/). If you want to deploy the program on a server, use `python manage.py runserver 0.0.0.0` instead. More info [here](https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/). 94 | 95 | [![Chatbot web interface](chatbot_miniature.png)](http://e-pot.xyz/cv/chatbot.png) 96 | 97 | ## Results 98 | 99 | Surprisingly, it's possible to get some results after only 1 or 2 hours of training (on a GeForce GT 740M), by drastically reducing the input sequence to 5 words and output to 3 (plus the `go` and `eos` tokens) and by using a small embedding size (something like 25). Since then I modified the code and now the output length has to match the input one but you can still reproduce the original results, using [this version](https://github.com/Conchylicultor/DeepQA/tree/92863e2929580818f866f16969909fe2093d41d1). 100 | Of course, the network won't be really chatty: 101 | 102 | Q: Hi 103 | A: Hi. 104 | 105 | Q: What is your name ? 106 | A: Laura. 107 | 108 | Q: What does that mean ? 109 | A: I dunno. 110 | 111 | Q: How old are you ? 112 | A: thirty-five. 113 | 114 | Q: Will Google hire me ? 115 | A: No. 116 | 117 | Q: Tell me the alphabet 118 | A: Fuck you. 119 | 120 | Q: That's not nice 121 | A: Yeah. 122 | 123 | Here is some case where it fails: 124 | 125 | Q: Who is Laura ? 126 | A: My brother. 127 | 128 | Q: Say 'goodbye' 129 | A: Alright. 130 | 131 | Q: What is cooking ? 132 | A: A channel. 133 | 134 | Q: Can you say no ? 135 | A: No. 136 | 137 | Q: Two plus two 138 | A: Manny... 139 | 140 | With longer sentences, the network is much slower to train. After 250000 iterations with maxLength=10, the network still mainly gives evasive answers (by answering most of the open ended questions by 'I don't know') but it seems to have integrated some basic semantic: 141 | 142 | Q: When are the fireworks ? 143 | A: Tomorrow. 144 | 145 | Q: What color ? 146 | A: Gray. 147 | 148 | Q: Which apple ? 149 | A: That's the first one. 150 | 151 | Q: What time is it ? 152 | A: Eleven o'clock. 153 | 154 | Q: Who is Skywalker ? 155 | A: She's a freelance bio-exorcist. 156 | 157 | Q: Who is John ? 158 | A: The old man. 159 | 160 | Q: Who is Jenny ? 161 | A: The man who killed herself. 162 | 163 | Q: When were you born ? 164 | A: Soon. 165 | 166 | As shown on the last questions, the network still does some confusion between the genders and the present/future. I also tried some deeper philosophical questions with more or less success. 167 | 168 | Q: What is the purpose of being intelligent ? 169 | A: The man are obsolete. 170 | 171 | Q: What is immoral ? 172 | A: The truth. 173 | 174 | Q: What is morality ? 175 | A: The worst. 176 | 177 | Q: What is the purpose of existence ? 178 | A: It 's private. 179 | 180 | Q: What happens if machines can think ? 181 | A: I think I'll just lie with you. 182 | 183 | The model I trained is quite limited by the embedding size I put and by its size, and by the training corpus size. Its 'thought vector' is also probably too small to answer the kind of following: 184 | 185 | Q: My name is David. What is my name ? 186 | A: Debbie. 187 | 188 | Q: My name is John. What is my name ? 189 | A: Carl Smith. 190 | 191 | Q: How many legs does a cat have ? 192 | A: Four. 193 | 194 | Q: How many legs does a spider have ? 195 | A: Four. 196 | 197 | Q: Do you prefer football or soccer ? 198 | A: Whiskey. 199 | 200 | It also seems to overfit as sometimes it will just pop out sentences from its training set unrelated to the question. Adding some dropout should reduce the issue. 201 | 202 | Q: I play tennis. What do I play ? 203 | A: I'm going to get laid. 204 | 205 | Q: What color is the sky ? 206 | A: One of the deputies is everywhere. 207 | 208 | ## Pretrained model (not working) 209 | 210 | Note: Links do not work anymore. 211 | 212 | ~~You can find a pre-trained model [here](https://drive.google.com/file/d/0Bw-phsNSkq23OXRFTkNqN0JGUU0/view?usp=sharing)~~, trained of the default corpus. To use it: 213 | 1. Extract the zip file inside `DeepQA/save/` 214 | 2. Copy the preprocessed dataset from `save/model-pretrainedv2/dataset-cornell-old-lenght10-filter0-vocabSize0.pkl` to `data/samples/`. 215 | 3. Run `./main.py --modelTag pretrainedv2 --test interactive`. 216 | 217 | Thanks to Nicholas C., [here](https://drive.google.com/drive/folders/0Bw-phsNSkq23c29ZQ2N6X3lyc1U?resourcekey=0-u1yOQjC701db6O9FfCW9EA&usp=sharing) ([original](https://mcastedu-my.sharepoint.com/personal/nicholas_cutajar_a100636_mcast_edu_mt/_layouts/15/guestaccess.aspx?folderid=077576c4cf9854642a968f67909380f45&authkey=AVt2JWMPkf2R_mWBpI1eAUY)) are some additional pre-trained models (compatible with TF 1.2) for diverse datasets. The folder also contains the pre-processed dataset for Cornell, OpenSubtitles, Ubuntu and Scotus (to move inside `data/samples/`). Those are required is you don't want to process the datasets yourself. 218 | 219 | If you have a high-end GPU, don't hesitate to play with the hyper-parameters/corpus to train a better model. From my experiments, it seems that the learning rate and dropout rate have the most impact on the results. Also if you want to share your models, don't hesitate to contact me and I'll add it here. 220 | 221 | ## Improvements 222 | 223 | In addition to trying larger/deeper model, there are a lot of small improvements which could be tested. Don't hesitate to send a pull request if you implement one of those. Here are some ideas: 224 | 225 | * For now, the predictions are deterministic (the network just take the most likely output) so when answering a question, the network will always gives the same answer. By adding a sampling mechanism, the network could give more diverse (and maybe more interesting) answers. The easiest way to do that is to sample the next predicted word from the SoftMax probability distribution. By combining that with the `loop_function` argument of `tf.nn.seq2seq.rnn_decoder`, it shouldn't be too difficult to add. After that, it should be possible to play with the SoftMax temperature to get more conservative or exotic predictions. 226 | * Adding attention could potentially improve the predictions, especially for longer sentences. It should be straightforward by replacing `embedding_rnn_seq2seq` by `embedding_attention_seq2seq` on `model.py`. 227 | * Having more data usually don't hurt. Training on a bigger corpus should be beneficial. [Reddit comments dataset](https://www.reddit.com/r/datasets/comments/59039y/updated_reddit_comment_dataset_up_to_201608/) seems the biggest for now (and is too big for this program to support it). Another trick to artificially increase the dataset size when creating the corpus could be to split the sentences of each training sample (ex: from the sample `Q:Sentence 1. Sentence 2. => A:Sentence X. Sentence Y.` we could generate 3 new samples: `Q:Sentence 1. Sentence 2. => A:Sentence X.`, `Q:Sentence 2. => A:Sentence X. Sentence Y.` and `Q:Sentence 2. => A:Sentence X.`. Warning: other combinations like `Q:Sentence 1. => A:Sentence X.` won't work because it would break the transition `2 => X` which links the question to the answer) 228 | * The testing curve should really be monitored as done in my other [music generation](https://github.com/Conchylicultor/MusicGenerator) project. This would greatly help to see the impact of dropout on overfitting. For now it's just done empirically by manually checking the testing prediction at different training steps. 229 | * For now, the questions are independent from each other. To link questions together, a straightforward way would be to feed all previous questions and answer to the encoder before giving the answer. Some caching could be done on the final encoder stated to avoid recomputing it each time. To improve the accuracy, the network should be retrain on entire dialogues instead of just individual QA. Also when feeding the previous dialogue to the encoder, new tokens `` and `` could be added so the encoder knows when the interlocutor is changing. I'm not sure though that the simple seq2seq model would be sufficient to capture long term dependencies between sentences. Adding a bucket system to group similar input lengths together could greatly improve training speed. 230 | -------------------------------------------------------------------------------- /chatbot/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ["chatbot"] 2 | -------------------------------------------------------------------------------- /chatbot/corpus/cornelldata.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Conchylicultor. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ============================================================================== 15 | 16 | import os 17 | import ast 18 | 19 | """ 20 | Load the cornell movie dialog corpus. 21 | 22 | Available from here: 23 | http://www.cs.cornell.edu/~cristian/Cornell_Movie-Dialogs_Corpus.html 24 | 25 | """ 26 | 27 | class CornellData: 28 | """ 29 | 30 | """ 31 | 32 | def __init__(self, dirName): 33 | """ 34 | Args: 35 | dirName (string): directory where to load the corpus 36 | """ 37 | self.lines = {} 38 | self.conversations = [] 39 | 40 | MOVIE_LINES_FIELDS = ["lineID","characterID","movieID","character","text"] 41 | MOVIE_CONVERSATIONS_FIELDS = ["character1ID","character2ID","movieID","utteranceIDs"] 42 | 43 | self.lines = self.loadLines(os.path.join(dirName, "movie_lines.txt"), MOVIE_LINES_FIELDS) 44 | self.conversations = self.loadConversations(os.path.join(dirName, "movie_conversations.txt"), MOVIE_CONVERSATIONS_FIELDS) 45 | 46 | # TODO: Cleaner program (merge copy-paste) !! 47 | 48 | def loadLines(self, fileName, fields): 49 | """ 50 | Args: 51 | fileName (str): file to load 52 | field (set): fields to extract 53 | Return: 54 | dict>: the extracted fields for each line 55 | """ 56 | lines = {} 57 | 58 | with open(fileName, 'r', encoding='iso-8859-1') as f: # TODO: Solve Iso encoding pb ! 59 | for line in f: 60 | values = line.split(" +++$+++ ") 61 | 62 | # Extract fields 63 | lineObj = {} 64 | for i, field in enumerate(fields): 65 | lineObj[field] = values[i] 66 | 67 | lines[lineObj['lineID']] = lineObj 68 | 69 | return lines 70 | 71 | def loadConversations(self, fileName, fields): 72 | """ 73 | Args: 74 | fileName (str): file to load 75 | field (set): fields to extract 76 | Return: 77 | dict>: the extracted fields for each line 78 | """ 79 | conversations = [] 80 | 81 | with open(fileName, 'r', encoding='iso-8859-1') as f: # TODO: Solve Iso encoding pb ! 82 | for line in f: 83 | values = line.split(" +++$+++ ") 84 | 85 | # Extract fields 86 | convObj = {} 87 | for i, field in enumerate(fields): 88 | convObj[field] = values[i] 89 | 90 | # Convert string to list (convObj["utteranceIDs"] == "['L598485', 'L598486', ...]") 91 | lineIds = ast.literal_eval(convObj["utteranceIDs"]) 92 | 93 | # Reassemble lines 94 | convObj["lines"] = [] 95 | for lineId in lineIds: 96 | convObj["lines"].append(self.lines[lineId]) 97 | 98 | conversations.append(convObj) 99 | 100 | return conversations 101 | 102 | def getConversations(self): 103 | return self.conversations 104 | -------------------------------------------------------------------------------- /chatbot/corpus/lightweightdata.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Conchylicultor. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ============================================================================== 15 | 16 | import os 17 | 18 | """ 19 | Load data from a dataset of simply-formatted data 20 | 21 | from A to B 22 | from B to A 23 | from A to B 24 | from B to A 25 | from A to B 26 | === 27 | from C to D 28 | from D to C 29 | from C to D 30 | from D to C 31 | from C to D 32 | from D to C 33 | ... 34 | 35 | `===` lines just separate linear conversations between 2 people. 36 | 37 | """ 38 | 39 | class LightweightData: 40 | """ 41 | """ 42 | 43 | def __init__(self, lightweightFile): 44 | """ 45 | Args: 46 | lightweightFile (string): file containing our lightweight-formatted corpus 47 | """ 48 | self.CONVERSATION_SEP = "===" 49 | self.conversations = [] 50 | self.loadLines(lightweightFile + '.txt') 51 | 52 | def loadLines(self, fileName): 53 | """ 54 | Args: 55 | fileName (str): file to load 56 | """ 57 | 58 | linesBuffer = [] 59 | with open(fileName, 'r') as f: 60 | for line in f: 61 | l = line.strip() 62 | if l == self.CONVERSATION_SEP: 63 | self.conversations.append({"lines": linesBuffer}) 64 | linesBuffer = [] 65 | else: 66 | linesBuffer.append({"text": l}) 67 | if len(linesBuffer): # Eventually flush the last conversation 68 | self.conversations.append({"lines": linesBuffer}) 69 | 70 | def getConversations(self): 71 | return self.conversations 72 | -------------------------------------------------------------------------------- /chatbot/corpus/opensubsdata.py: -------------------------------------------------------------------------------- 1 | # Based on code from https://github.com/AlJohri/OpenSubtitles 2 | # by Al Johri 3 | 4 | import xml.etree.ElementTree as ET 5 | import datetime 6 | import os 7 | import sys 8 | import json 9 | import re 10 | import pprint 11 | 12 | from gzip import GzipFile 13 | from tqdm import tqdm 14 | 15 | """ 16 | Load the opensubtitles dialog corpus. 17 | """ 18 | 19 | class OpensubsData: 20 | """ 21 | 22 | """ 23 | 24 | def __init__(self, dirName): 25 | """ 26 | Args: 27 | dirName (string): directory where to load the corpus 28 | """ 29 | 30 | # Hack this to filter on subset of Opensubtitles 31 | # dirName = "%s/en/Action" % dirName 32 | 33 | print("Loading OpenSubtitles conversations in %s." % dirName) 34 | self.conversations = [] 35 | self.tag_re = re.compile(r'(|<[^>]*>)') 36 | self.conversations = self.loadConversations(dirName) 37 | 38 | def loadConversations(self, dirName): 39 | """ 40 | Args: 41 | dirName (str): folder to load 42 | Return: 43 | array(question, answer): the extracted QA pairs 44 | """ 45 | conversations = [] 46 | dirList = self.filesInDir(dirName) 47 | for filepath in tqdm(dirList, "OpenSubtitles data files"): 48 | if filepath.endswith('gz'): 49 | try: 50 | doc = self.getXML(filepath) 51 | conversations.extend(self.genList(doc)) 52 | except ValueError: 53 | tqdm.write("Skipping file %s with errors." % filepath) 54 | except: 55 | print("Unexpected error:", sys.exc_info()[0]) 56 | raise 57 | return conversations 58 | 59 | def getConversations(self): 60 | return self.conversations 61 | 62 | def genList(self, tree): 63 | root = tree.getroot() 64 | 65 | timeFormat = '%H:%M:%S' 66 | maxDelta = datetime.timedelta(seconds=1) 67 | 68 | startTime = datetime.datetime.min 69 | strbuf = '' 70 | sentList = [] 71 | 72 | for child in root: 73 | for elem in child: 74 | if elem.tag == 'time': 75 | elemID = elem.attrib['id'] 76 | elemVal = elem.attrib['value'][:-4] 77 | if elemID[-1] == 'S': 78 | startTime = datetime.datetime.strptime(elemVal, timeFormat) 79 | else: 80 | sentList.append((strbuf.strip(), startTime, datetime.datetime.strptime(elemVal, timeFormat))) 81 | strbuf = '' 82 | else: 83 | try: 84 | strbuf = strbuf + " " + elem.text 85 | except: 86 | pass 87 | 88 | conversations = [] 89 | for idx in range(0, len(sentList) - 1): 90 | cur = sentList[idx] 91 | nxt = sentList[idx + 1] 92 | if nxt[1] - cur[2] <= maxDelta and cur and nxt: 93 | tmp = {} 94 | tmp["lines"] = [] 95 | tmp["lines"].append(self.getLine(cur[0])) 96 | tmp["lines"].append(self.getLine(nxt[0])) 97 | if self.filter(tmp): 98 | conversations.append(tmp) 99 | 100 | return conversations 101 | 102 | def getLine(self, sentence): 103 | line = {} 104 | line["text"] = self.tag_re.sub('', sentence).replace('\\\'','\'').strip().lower() 105 | return line 106 | 107 | def filter(self, lines): 108 | # Use the followint to customize filtering of QA pairs 109 | # 110 | # startwords = ("what", "how", "when", "why", "where", "do", "did", "is", "are", "can", "could", "would", "will") 111 | # question = lines["lines"][0]["text"] 112 | # if not question.endswith('?'): 113 | # return False 114 | # if not question.split(' ')[0] in startwords: 115 | # return False 116 | # 117 | return True 118 | 119 | def getXML(self, filepath): 120 | fext = os.path.splitext(filepath)[1] 121 | if fext == '.gz': 122 | tmp = GzipFile(filename=filepath) 123 | return ET.parse(tmp) 124 | else: 125 | return ET.parse(filepath) 126 | 127 | def filesInDir(self, dirname): 128 | result = [] 129 | for dirpath, dirs, files in os.walk(dirname): 130 | for filename in files: 131 | fname = os.path.join(dirpath, filename) 132 | result.append(fname) 133 | return result 134 | -------------------------------------------------------------------------------- /chatbot/corpus/scotusdata.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Conchylicultor. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ============================================================================== 15 | 16 | import os 17 | 18 | """ 19 | Load transcripts from the Supreme Court of the USA. 20 | 21 | Available from here: 22 | https://github.com/pender/chatbot-rnn 23 | 24 | """ 25 | 26 | class ScotusData: 27 | """ 28 | """ 29 | 30 | def __init__(self, dirName): 31 | """ 32 | Args: 33 | dirName (string): directory where to load the corpus 34 | """ 35 | self.lines = self.loadLines(os.path.join(dirName, "scotus")) 36 | self.conversations = [{"lines": self.lines}] 37 | 38 | 39 | def loadLines(self, fileName): 40 | """ 41 | Args: 42 | fileName (str): file to load 43 | Return: 44 | list>: the extracted fields for each line 45 | """ 46 | lines = [] 47 | 48 | with open(fileName, 'r') as f: 49 | for line in f: 50 | l = line[line.index(":")+1:].strip() # Strip name of speaker. 51 | 52 | lines.append({"text": l}) 53 | 54 | return lines 55 | 56 | 57 | def getConversations(self): 58 | return self.conversations 59 | -------------------------------------------------------------------------------- /chatbot/corpus/ubuntudata.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Conchylicultor. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # ============================================================================== 15 | 16 | import os 17 | 18 | from tqdm import tqdm 19 | 20 | """ 21 | Ubuntu Dialogue Corpus 22 | 23 | http://arxiv.org/abs/1506.08909 24 | 25 | """ 26 | 27 | class UbuntuData: 28 | """ 29 | """ 30 | 31 | def __init__(self, dirName): 32 | """ 33 | Args: 34 | dirName (string): directory where to load the corpus 35 | """ 36 | self.MAX_NUMBER_SUBDIR = 10 37 | self.conversations = [] 38 | __dir = os.path.join(dirName, "dialogs") 39 | number_subdir = 0 40 | for sub in tqdm(os.scandir(__dir), desc="Ubuntu dialogs subfolders", total=len(os.listdir(__dir))): 41 | if number_subdir == self.MAX_NUMBER_SUBDIR: 42 | print("WARNING: Early stoping, only extracting {} directories".format(self.MAX_NUMBER_SUBDIR)) 43 | return 44 | 45 | if sub.is_dir(): 46 | number_subdir += 1 47 | for f in os.scandir(sub.path): 48 | if f.name.endswith(".tsv"): 49 | self.conversations.append({"lines": self.loadLines(f.path)}) 50 | 51 | 52 | def loadLines(self, fileName): 53 | """ 54 | Args: 55 | fileName (str): file to load 56 | Return: 57 | list>: the extracted fields for each line 58 | """ 59 | lines = [] 60 | with open(fileName, 'r') as f: 61 | for line in f: 62 | l = line[line.rindex("\t")+1:].strip() # Strip metadata (timestamps, speaker names) 63 | 64 | lines.append({"text": l}) 65 | 66 | return lines 67 | 68 | 69 | def getConversations(self): 70 | return self.conversations 71 | -------------------------------------------------------------------------------- /chatbot/model.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Conchylicultor. All Rights Reserved. 2 | # Modifications copyright (C) 2016 Carlos Segura 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # ============================================================================== 16 | 17 | """ 18 | Model to predict the next sentence given an input sequence 19 | 20 | """ 21 | 22 | import tensorflow as tf 23 | 24 | from chatbot.textdata import Batch 25 | 26 | 27 | class ProjectionOp: 28 | """ Single layer perceptron 29 | Project input tensor on the output dimension 30 | """ 31 | def __init__(self, shape, scope=None, dtype=None): 32 | """ 33 | Args: 34 | shape: a tuple (input dim, output dim) 35 | scope (str): encapsulate variables 36 | dtype: the weights type 37 | """ 38 | assert len(shape) == 2 39 | 40 | self.scope = scope 41 | 42 | # Projection on the keyboard 43 | with tf.variable_scope('weights_' + self.scope): 44 | self.W_t = tf.get_variable( 45 | 'weights', 46 | shape, 47 | # initializer=tf.truncated_normal_initializer() # TODO: Tune value (fct of input size: 1/sqrt(input_dim)) 48 | dtype=dtype 49 | ) 50 | self.b = tf.get_variable( 51 | 'bias', 52 | shape[0], 53 | initializer=tf.constant_initializer(), 54 | dtype=dtype 55 | ) 56 | self.W = tf.transpose(self.W_t) 57 | 58 | def getWeights(self): 59 | """ Convenience method for some tf arguments 60 | """ 61 | return self.W, self.b 62 | 63 | def __call__(self, X): 64 | """ Project the output of the decoder into the vocabulary space 65 | Args: 66 | X (tf.Tensor): input value 67 | """ 68 | with tf.name_scope(self.scope): 69 | return tf.matmul(X, self.W) + self.b 70 | 71 | 72 | class Model: 73 | """ 74 | Implementation of a seq2seq model. 75 | Architecture: 76 | Encoder/decoder 77 | 2 LTSM layers 78 | """ 79 | 80 | def __init__(self, args, textData): 81 | """ 82 | Args: 83 | args: parameters of the model 84 | textData: the dataset object 85 | """ 86 | print("Model creation...") 87 | 88 | self.textData = textData # Keep a reference on the dataset 89 | self.args = args # Keep track of the parameters of the model 90 | self.dtype = tf.float32 91 | 92 | # Placeholders 93 | self.encoderInputs = None 94 | self.decoderInputs = None # Same that decoderTarget plus the 95 | self.decoderTargets = None 96 | self.decoderWeights = None # Adjust the learning to the target sentence size 97 | 98 | # Main operators 99 | self.lossFct = None 100 | self.optOp = None 101 | self.outputs = None # Outputs of the network, list of probability for each words 102 | 103 | # Construct the graphs 104 | self.buildNetwork() 105 | 106 | def buildNetwork(self): 107 | """ Create the computational graph 108 | """ 109 | 110 | # TODO: Create name_scopes (for better graph visualisation) 111 | # TODO: Use buckets (better perfs) 112 | 113 | # Parameters of sampled softmax (needed for attention mechanism and a large vocabulary size) 114 | outputProjection = None 115 | # Sampled softmax only makes sense if we sample less than vocabulary size. 116 | if 0 < self.args.softmaxSamples < self.textData.getVocabularySize(): 117 | outputProjection = ProjectionOp( 118 | (self.textData.getVocabularySize(), self.args.hiddenSize), 119 | scope='softmax_projection', 120 | dtype=self.dtype 121 | ) 122 | 123 | def sampledSoftmax(labels, inputs): 124 | labels = tf.reshape(labels, [-1, 1]) # Add one dimension (nb of true classes, here 1) 125 | 126 | # We need to compute the sampled_softmax_loss using 32bit floats to 127 | # avoid numerical instabilities. 128 | localWt = tf.cast(outputProjection.W_t, tf.float32) 129 | localB = tf.cast(outputProjection.b, tf.float32) 130 | localInputs = tf.cast(inputs, tf.float32) 131 | 132 | return tf.cast( 133 | tf.nn.sampled_softmax_loss( 134 | localWt, # Should have shape [num_classes, dim] 135 | localB, 136 | labels, 137 | localInputs, 138 | self.args.softmaxSamples, # The number of classes to randomly sample per batch 139 | self.textData.getVocabularySize()), # The number of classes 140 | self.dtype) 141 | 142 | # Creation of the rnn cell 143 | def create_rnn_cell(): 144 | encoDecoCell = tf.contrib.rnn.BasicLSTMCell( # Or GRUCell, LSTMCell(args.hiddenSize) 145 | self.args.hiddenSize, 146 | ) 147 | if not self.args.test: # TODO: Should use a placeholder instead 148 | encoDecoCell = tf.contrib.rnn.DropoutWrapper( 149 | encoDecoCell, 150 | input_keep_prob=1.0, 151 | output_keep_prob=self.args.dropout 152 | ) 153 | return encoDecoCell 154 | encoDecoCell = tf.contrib.rnn.MultiRNNCell( 155 | [create_rnn_cell() for _ in range(self.args.numLayers)], 156 | ) 157 | 158 | # Network input (placeholders) 159 | 160 | with tf.name_scope('placeholder_encoder'): 161 | self.encoderInputs = [tf.placeholder(tf.int32, [None, ]) for _ in range(self.args.maxLengthEnco)] # Batch size * sequence length * input dim 162 | 163 | with tf.name_scope('placeholder_decoder'): 164 | self.decoderInputs = [tf.placeholder(tf.int32, [None, ], name='inputs') for _ in range(self.args.maxLengthDeco)] # Same sentence length for input and output (Right ?) 165 | self.decoderTargets = [tf.placeholder(tf.int32, [None, ], name='targets') for _ in range(self.args.maxLengthDeco)] 166 | self.decoderWeights = [tf.placeholder(tf.float32, [None, ], name='weights') for _ in range(self.args.maxLengthDeco)] 167 | 168 | # Define the network 169 | # Here we use an embedding model, it takes integer as input and convert them into word vector for 170 | # better word representation 171 | decoderOutputs, states = tf.contrib.legacy_seq2seq.embedding_rnn_seq2seq( 172 | self.encoderInputs, # List<[batch=?, inputDim=1]>, list of size args.maxLength 173 | self.decoderInputs, # For training, we force the correct output (feed_previous=False) 174 | encoDecoCell, 175 | self.textData.getVocabularySize(), 176 | self.textData.getVocabularySize(), # Both encoder and decoder have the same number of class 177 | embedding_size=self.args.embeddingSize, # Dimension of each word 178 | output_projection=outputProjection.getWeights() if outputProjection else None, 179 | feed_previous=bool(self.args.test) # When we test (self.args.test), we use previous output as next input (feed_previous) 180 | ) 181 | 182 | # TODO: When the LSTM hidden size is too big, we should project the LSTM output into a smaller space (4086 => 2046): Should speed up 183 | # training and reduce memory usage. Other solution, use sampling softmax 184 | 185 | # For testing only 186 | if self.args.test: 187 | if not outputProjection: 188 | self.outputs = decoderOutputs 189 | else: 190 | self.outputs = [outputProjection(output) for output in decoderOutputs] 191 | 192 | # TODO: Attach a summary to visualize the output 193 | 194 | # For training only 195 | else: 196 | # Finally, we define the loss function 197 | self.lossFct = tf.contrib.legacy_seq2seq.sequence_loss( 198 | decoderOutputs, 199 | self.decoderTargets, 200 | self.decoderWeights, 201 | self.textData.getVocabularySize(), 202 | softmax_loss_function= sampledSoftmax if outputProjection else None # If None, use default SoftMax 203 | ) 204 | tf.summary.scalar('loss', self.lossFct) # Keep track of the cost 205 | 206 | # Initialize the optimizer 207 | opt = tf.train.AdamOptimizer( 208 | learning_rate=self.args.learningRate, 209 | beta1=0.9, 210 | beta2=0.999, 211 | epsilon=1e-08 212 | ) 213 | self.optOp = opt.minimize(self.lossFct) 214 | 215 | def step(self, batch): 216 | """ Forward/training step operation. 217 | Does not perform run on itself but just return the operators to do so. Those have then to be run 218 | Args: 219 | batch (Batch): Input data on testing mode, input and target on output mode 220 | Return: 221 | (ops), dict: A tuple of the (training, loss) operators or (outputs,) in testing mode with the associated feed dictionary 222 | """ 223 | 224 | # Feed the dictionary 225 | feedDict = {} 226 | ops = None 227 | 228 | if not self.args.test: # Training 229 | for i in range(self.args.maxLengthEnco): 230 | feedDict[self.encoderInputs[i]] = batch.encoderSeqs[i] 231 | for i in range(self.args.maxLengthDeco): 232 | feedDict[self.decoderInputs[i]] = batch.decoderSeqs[i] 233 | feedDict[self.decoderTargets[i]] = batch.targetSeqs[i] 234 | feedDict[self.decoderWeights[i]] = batch.weights[i] 235 | 236 | ops = (self.optOp, self.lossFct) 237 | else: # Testing (batchSize == 1) 238 | for i in range(self.args.maxLengthEnco): 239 | feedDict[self.encoderInputs[i]] = batch.encoderSeqs[i] 240 | feedDict[self.decoderInputs[0]] = [self.textData.goToken] 241 | 242 | ops = (self.outputs,) 243 | 244 | # Return one pass operator 245 | return ops, feedDict 246 | -------------------------------------------------------------------------------- /chatbot/trainner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2015 Conchylicultor. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # ============================================================================== 17 | 18 | """ 19 | Train the program by launching it with random parametters 20 | """ 21 | 22 | from tqdm import tqdm 23 | import os 24 | 25 | 26 | def main(): 27 | """ 28 | Launch the training with different parametters 29 | """ 30 | 31 | # TODO: define: 32 | # step+noize 33 | # log scale instead of uniform 34 | 35 | # Define parametter: [min, max] 36 | dictParams = { 37 | "batchSize": [int, [1, 3]] 38 | "learningRate": [float, [1, 3]] 39 | } 40 | 41 | # Training multiple times with different parametters 42 | for i in range(10): 43 | # Generate the command line arguments 44 | trainingArgs = "" 45 | for keyArg, valueArg in dictParams: 46 | value = str(random(valueArg[0], max=valueArg[1])) 47 | trainingArgs += " --" + keyArg + " " + value 48 | 49 | # Launch the program 50 | os.run("main.py" + trainingArgs) 51 | 52 | # TODO: Save params/results ? or already inside training args ? 53 | 54 | 55 | if __name__ == "__main__": 56 | main() 57 | -------------------------------------------------------------------------------- /chatbot_miniature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conchylicultor/DeepQA/886ec77ac49ff70be1b6d4de30fffe6a96480a4f/chatbot_miniature.png -------------------------------------------------------------------------------- /chatbot_website/.gitignore: -------------------------------------------------------------------------------- 1 | # Database 2 | *.sqlite3 3 | # Redis 4 | dump.rdb 5 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'chatbot_interface.chatbotmanager.ChatbotManager' 2 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ChatbotInterfaceConfig(AppConfig): 5 | name = 'chatbot_interface' 6 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/chatbotmanager.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | import logging 3 | import sys 4 | 5 | from django.apps import AppConfig 6 | import sys 7 | import os 8 | 9 | chatbotPath = "/".join(settings.BASE_DIR.split('/')[:-1]) 10 | sys.path.append(chatbotPath) 11 | from chatbot import chatbot 12 | 13 | 14 | logger = logging.getLogger(__name__) 15 | 16 | 17 | class ChatbotManager(AppConfig): 18 | """ Manage a single instance of the chatbot shared over the website 19 | """ 20 | name = 'chatbot_interface' 21 | verbose_name = 'Chatbot Interface' 22 | 23 | bot = None 24 | 25 | def ready(self): 26 | """ Called by Django only once during startup 27 | """ 28 | # Initialize the chatbot daemon (should be launched only once) 29 | if (os.environ.get('RUN_MAIN') == 'true' and # HACK: Avoid the autoreloader executing the startup code twice (could also use: python manage.py runserver --noreload) (see http://stackoverflow.com/questions/28489863/why-is-run-called-twice-in-the-django-dev-server) 30 | not any(x in sys.argv for x in ['makemigrations', 'migrate'])): # HACK: Avoid initialisation while migrate 31 | ChatbotManager.initBot() 32 | 33 | @staticmethod 34 | def initBot(): 35 | """ Instantiate the chatbot for later use 36 | Should be called only once 37 | """ 38 | if not ChatbotManager.bot: 39 | logger.info('Initializing bot...') 40 | ChatbotManager.bot = chatbot.Chatbot() 41 | ChatbotManager.bot.main(['--modelTag', 'server', '--test', 'daemon', '--rootDir', chatbotPath]) 42 | else: 43 | logger.info('Bot already initialized.') 44 | 45 | @staticmethod 46 | def callBot(sentence): 47 | """ Use the previously instantiated bot to predict a response to the given sentence 48 | Args: 49 | sentence (str): the question to answer 50 | Return: 51 | str: the answer 52 | """ 53 | if ChatbotManager.bot: 54 | return ChatbotManager.bot.daemonPredict(sentence) 55 | else: 56 | logger.error('Error: Bot not initialized!') 57 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/consumer.py: -------------------------------------------------------------------------------- 1 | from channels import Group 2 | from channels.sessions import channel_session 3 | import logging 4 | import sys 5 | import json 6 | 7 | from .chatbotmanager import ChatbotManager 8 | 9 | 10 | logger = logging.getLogger(__name__) 11 | 12 | 13 | def _getClientName(client): 14 | """ Return the unique id for the client 15 | Args: 16 | client list<>: the client which send the message of the from [ip (str), port (int)] 17 | Return: 18 | str: the id associated with the client 19 | """ 20 | return 'room-' + client[0] + '-' + str(client[1]) 21 | 22 | 23 | @channel_session 24 | def ws_connect(message): 25 | """ Called when a client try to open a WebSocket 26 | Args: 27 | message (Obj): object containing the client query 28 | """ 29 | if message['path'] == '/chat': # Check we are on the right channel 30 | clientName = _getClientName(message['client']) 31 | logger.info('New client connected: {}'.format(clientName)) 32 | Group(clientName).add(message.reply_channel) # Answer back to the client 33 | message.channel_session['room'] = clientName 34 | message.reply_channel.send({'accept': True}) 35 | 36 | 37 | @channel_session 38 | def ws_receive(message): 39 | """ Called when a client send a message 40 | Args: 41 | message (Obj): object containing the client query 42 | """ 43 | # Get client info 44 | clientName = message.channel_session['room'] 45 | data = json.loads(message['text']) 46 | 47 | # Compute the prediction 48 | question = data['message'] 49 | try: 50 | answer = ChatbotManager.callBot(question) 51 | except: # Catching all possible mistakes 52 | logger.error('{}: Error with this question {}'.format(clientName, question)) 53 | logger.error("Unexpected error:", sys.exc_info()[0]) 54 | answer = 'Error: Internal problem' 55 | 56 | # Check eventual error 57 | if not answer: 58 | answer = 'Error: Try a shorter sentence' 59 | 60 | logger.info('{}: {} -> {}'.format(clientName, question, answer)) 61 | 62 | # Send the prediction back 63 | Group(clientName).send({'text': json.dumps({'message': answer})}) 64 | 65 | @channel_session 66 | def ws_disconnect(message): 67 | """ Called when a client disconnect 68 | Args: 69 | message (Obj): object containing the client query 70 | """ 71 | clientName = message.channel_session['room'] 72 | logger.info('Client disconnected: {}'.format(clientName)) 73 | Group(clientName).discard(message.reply_channel) 74 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conchylicultor/DeepQA/886ec77ac49ff70be1b6d4de30fffe6a96480a4f/chatbot_website/chatbot_interface/migrations/__init__.py -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/routing.py: -------------------------------------------------------------------------------- 1 | from . import consumer 2 | 3 | channel_routing = { 4 | # TODO: From the original examples, there is more (https://github.com/jacobian/channels-example/) 5 | 'websocket.connect': consumer.ws_connect, 6 | 'websocket.receive': consumer.ws_receive, 7 | 'websocket.disconnect': consumer.ws_disconnect, 8 | } 9 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Creative Commons Attribution 3.0 Unported 2 | http://creativecommons.org/licenses/by/3.0/ 3 | 4 | License 5 | 6 | THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. 7 | 8 | BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. 9 | 10 | 1. Definitions 11 | 12 | 1. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. 13 | 2. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License. 14 | 3. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. 15 | 4. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. 16 | 5. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. 17 | 6. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. 18 | 7. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. 19 | 8. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. 20 | 9. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. 21 | 22 | 2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. 23 | 24 | 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: 25 | 26 | 1. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; 27 | 2. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; 28 | 3. to Distribute and Publicly Perform the Work including as incorporated in Collections; and, 29 | 4. to Distribute and Publicly Perform Adaptations. 30 | 5. 31 | 32 | For the avoidance of doubt: 33 | 1. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; 34 | 2. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, 35 | 3. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. 36 | 37 | The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved. 38 | 39 | 4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: 40 | 41 | 1. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(b), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(b), as requested. 42 | 2. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4 (b) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. 43 | 3. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. 44 | 45 | 5. Representations, Warranties and Disclaimer 46 | 47 | UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. 48 | 49 | 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 50 | 51 | 7. Termination 52 | 53 | 1. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. 54 | 2. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. 55 | 56 | 8. Miscellaneous 57 | 58 | 1. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. 59 | 2. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. 60 | 3. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. 61 | 4. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. 62 | 5. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. 63 | 6. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. 64 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/README.txt: -------------------------------------------------------------------------------- 1 | Identity by HTML5 UP 2 | html5up.net | @ajlkn 3 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 4 | 5 | 6 | Just a fun little profile/card-style template I whipped up during a break between major 7 | projects. Minimal, responsive, and powered by Skel + Sass. Enjoy :) 8 | 9 | Demo images* courtesy of Unsplash, a radtastic collection of CC0 (public domain) images 10 | you can use for pretty much whatever. 11 | 12 | (* = not included) 13 | 14 | AJ 15 | aj@lkn.io | @ajlkn 16 | 17 | 18 | Credits: 19 | 20 | Demo Images: 21 | Unsplash (unsplash.com) 22 | 23 | Icons: 24 | Font Awesome (fortawesome.github.com/Font-Awesome) 25 | 26 | Other: 27 | html5shiv.js (@afarkas @jdalton @jon_neal @rem) 28 | CSS3 Pie (css3pie.com) 29 | Respond.js (j.mp/respondjs) 30 | Skel (skel.io) -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/css/ie8.css: -------------------------------------------------------------------------------- 1 | /* 2 | Identity by HTML5 UP 3 | html5up.net | @ajlkn 4 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | */ 6 | 7 | /* List */ 8 | 9 | ul.icons li a { 10 | -ms-behavior: url("assets/js/PIE.htc"); 11 | } 12 | 13 | ul.icons li a:before { 14 | text-align: center; 15 | font-size: 26px; 16 | } 17 | 18 | /* Main */ 19 | 20 | #main { 21 | -ms-behavior: url("assets/js/PIE.htc"); 22 | } 23 | 24 | #main .avatar img { 25 | -ms-behavior: url("assets/js/PIE.htc"); 26 | } 27 | 28 | /* Footer */ 29 | 30 | #footer { 31 | color: #fff; 32 | } 33 | 34 | #footer .copyright li { 35 | border-left: solid 1px #fff; 36 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/css/ie9.css: -------------------------------------------------------------------------------- 1 | /* 2 | Identity by HTML5 UP 3 | html5up.net | @ajlkn 4 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | */ 6 | 7 | /* Basic */ 8 | 9 | body { 10 | background-image: url("../../images/bg.jpg"); 11 | background-repeat: no-repeat; 12 | background-size: cover; 13 | background-position: bottom center; 14 | background-attachment: fixed; 15 | } 16 | 17 | body:after { 18 | display: none; 19 | } 20 | 21 | /* List */ 22 | 23 | ul.icons li a:before { 24 | color: #c8cccf; 25 | } 26 | 27 | ul.icons li a:hover:before { 28 | color: #ff7496; 29 | } 30 | 31 | /* Wrapper */ 32 | 33 | #wrapper { 34 | text-align: center; 35 | } 36 | 37 | /* Main */ 38 | 39 | #main { 40 | display: inline-block; 41 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/css/images/overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conchylicultor/DeepQA/886ec77ac49ff70be1b6d4de30fffe6a96480a4f/chatbot_website/chatbot_interface/static/assets/css/images/overlay.png -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/css/main.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | @import url(font-awesome.min.css); 3 | @import url("http://fonts.googleapis.com/css?family=Source+Sans+Pro:300"); 4 | 5 | /* 6 | Identity by HTML5 UP 7 | html5up.net | @ajlkn 8 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 9 | */ 10 | 11 | /* Reset */ 12 | 13 | html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { 14 | margin: 0; 15 | padding: 0; 16 | border: 0; 17 | font-size: 100%; 18 | font: inherit; 19 | vertical-align: baseline; 20 | } 21 | 22 | article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { 23 | display: block; 24 | } 25 | 26 | body { 27 | line-height: 1; 28 | } 29 | 30 | ol, ul { 31 | list-style: none; 32 | } 33 | 34 | blockquote, q { 35 | quotes: none; 36 | } 37 | 38 | blockquote:before, blockquote:after, q:before, q:after { 39 | content: ''; 40 | content: none; 41 | } 42 | 43 | table { 44 | border-collapse: collapse; 45 | border-spacing: 0; 46 | } 47 | 48 | body { 49 | -webkit-text-size-adjust: none; 50 | } 51 | 52 | /* Box Model */ 53 | 54 | *, *:before, *:after { 55 | -moz-box-sizing: border-box; 56 | -webkit-box-sizing: border-box; 57 | box-sizing: border-box; 58 | } 59 | 60 | /* Basic */ 61 | 62 | @media screen and (max-width: 480px) { 63 | 64 | html, body { 65 | min-width: 320px; 66 | } 67 | 68 | } 69 | 70 | body.is-loading *, body.is-loading *:before, body.is-loading *:after { 71 | -moz-animation: none !important; 72 | -webkit-animation: none !important; 73 | -ms-animation: none !important; 74 | animation: none !important; 75 | -moz-transition: none !important; 76 | -webkit-transition: none !important; 77 | -ms-transition: none !important; 78 | transition: none !important; 79 | } 80 | 81 | html { 82 | height: 100%; 83 | } 84 | 85 | body { 86 | height: 100%; 87 | background-color: #ffffff; 88 | background-image: url("images/overlay.png"), -moz-linear-gradient(60deg, rgba(255, 165, 150, 0.5) 5%, rgba(0, 228, 255, 0.35)), url("../../images/bg.png"); 89 | background-image: url("images/overlay.png"), -webkit-linear-gradient(60deg, rgba(255, 165, 150, 0.5) 5%, rgba(0, 228, 255, 0.35)), url("../../images/bg.png"); 90 | background-image: url("images/overlay.png"), -ms-linear-gradient(60deg, rgba(255, 165, 150, 0.5) 5%, rgba(0, 228, 255, 0.35)), url("../../images/bg.png"); 91 | background-image: url("images/overlay.png"), linear-gradient(60deg, rgba(255, 165, 150, 0.5) 5%, rgba(0, 228, 255, 0.35)), url("../../images/bg.png"); 92 | background-repeat: repeat, no-repeat, no-repeat; 93 | background-size: 100px 100px, cover, cover; 94 | background-position: top left, center center, bottom center; 95 | background-attachment: fixed, fixed, fixed; 96 | } 97 | 98 | body:after { 99 | content: ''; 100 | display: block; 101 | position: fixed; 102 | top: 0; 103 | left: 0; 104 | width: 100%; 105 | height: inherit; 106 | opacity: 0; 107 | z-index: 1; 108 | background-color: #ffffff; 109 | background-image: url("images/overlay.png"), -moz-linear-gradient(60deg, rgba(255, 165, 150, 0.5) 5%, rgba(0, 228, 255, 0.35)); 110 | background-image: url("images/overlay.png"), -webkit-linear-gradient(60deg, rgba(255, 165, 150, 0.5) 5%, rgba(0, 228, 255, 0.35)); 111 | background-image: url("images/overlay.png"), -ms-linear-gradient(60deg, rgba(255, 165, 150, 0.5) 5%, rgba(0, 228, 255, 0.35)); 112 | background-image: url("images/overlay.png"), linear-gradient(60deg, rgba(255, 165, 150, 0.5) 5%, rgba(0, 228, 255, 0.35)); 113 | background-repeat: repeat, no-repeat; 114 | background-size: 100px 100px, cover; 115 | background-position: top left, center center; 116 | -moz-transition: opacity 1.75s ease-out; 117 | -webkit-transition: opacity 1.75s ease-out; 118 | -ms-transition: opacity 1.75s ease-out; 119 | transition: opacity 1.75s ease-out; 120 | } 121 | 122 | body.is-loading:after { 123 | opacity: 1; 124 | } 125 | 126 | /* Type */ 127 | 128 | body, input, select, textarea { 129 | color: #414f57; 130 | font-family: "Source Sans Pro", Helvetica, sans-serif; 131 | font-size: 14pt; 132 | font-weight: 300; 133 | line-height: 2; 134 | letter-spacing: 0.2em; 135 | text-transform: uppercase; 136 | } 137 | 138 | @media screen and (max-width: 1680px) { 139 | 140 | body, input, select, textarea { 141 | font-size: 11pt; 142 | } 143 | 144 | } 145 | 146 | @media screen and (max-width: 480px) { 147 | 148 | body, input, select, textarea { 149 | font-size: 10pt; 150 | line-height: 1.75; 151 | } 152 | 153 | } 154 | 155 | a { 156 | -moz-transition: color 0.2s ease, border-color 0.2s ease; 157 | -webkit-transition: color 0.2s ease, border-color 0.2s ease; 158 | -ms-transition: color 0.2s ease, border-color 0.2s ease; 159 | transition: color 0.2s ease, border-color 0.2s ease; 160 | color: inherit; 161 | text-decoration: none; 162 | } 163 | 164 | a:before { 165 | -moz-transition: color 0.2s ease, text-shadow 0.2s ease; 166 | -webkit-transition: color 0.2s ease, text-shadow 0.2s ease; 167 | -ms-transition: color 0.2s ease, text-shadow 0.2s ease; 168 | transition: color 0.2s ease, text-shadow 0.2s ease; 169 | } 170 | 171 | a:hover { 172 | color: #ff7496; 173 | } 174 | 175 | strong, b { 176 | color: #313f47; 177 | } 178 | 179 | em, i { 180 | font-style: italic; 181 | } 182 | 183 | p { 184 | margin: 0 0 1.5em 0; 185 | } 186 | 187 | h1, h2, h3, h4, h5, h6 { 188 | color: #313f47; 189 | line-height: 1.5; 190 | margin: 0 0 0.75em 0; 191 | } 192 | 193 | h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { 194 | color: inherit; 195 | text-decoration: none; 196 | } 197 | 198 | h1 { 199 | font-size: 1.85em; 200 | letter-spacing: 0.22em; 201 | margin: 0 0 0.525em 0; 202 | } 203 | 204 | h2 { 205 | font-size: 1.25em; 206 | } 207 | 208 | h3 { 209 | font-size: 1em; 210 | } 211 | 212 | h4 { 213 | font-size: 1em; 214 | } 215 | 216 | h5 { 217 | font-size: 1em; 218 | } 219 | 220 | h6 { 221 | font-size: 1em; 222 | } 223 | 224 | @media screen and (max-width: 480px) { 225 | 226 | h1 { 227 | font-size: 1.65em; 228 | } 229 | 230 | } 231 | 232 | sub { 233 | font-size: 0.8em; 234 | position: relative; 235 | top: 0.5em; 236 | } 237 | 238 | sup { 239 | font-size: 0.8em; 240 | position: relative; 241 | top: -0.5em; 242 | } 243 | 244 | hr { 245 | border: 0; 246 | border-bottom: solid 1px #c8cccf; 247 | margin: 3em 0; 248 | } 249 | 250 | /* Form */ 251 | 252 | form { 253 | margin: 0 0 1.5em 0; 254 | } 255 | 256 | form > .field { 257 | margin: 0 0 1.5em 0; 258 | } 259 | 260 | form > .field > :last-child { 261 | margin-bottom: 0; 262 | } 263 | 264 | label { 265 | color: #313f47; 266 | display: block; 267 | font-size: 0.9em; 268 | margin: 0 0 0.75em 0; 269 | } 270 | 271 | input[type="text"], 272 | input[type="password"], 273 | input[type="email"], 274 | input[type="tel"], 275 | select, 276 | textarea { 277 | -moz-appearance: none; 278 | -webkit-appearance: none; 279 | -ms-appearance: none; 280 | appearance: none; 281 | border-radius: 4px; 282 | border: solid 1px #c8cccf; 283 | color: inherit; 284 | display: block; 285 | outline: 0; 286 | padding: 0 1em; 287 | text-decoration: none; 288 | width: 100%; 289 | } 290 | 291 | input[type="text"]:invalid, 292 | input[type="password"]:invalid, 293 | input[type="email"]:invalid, 294 | input[type="tel"]:invalid, 295 | select:invalid, 296 | textarea:invalid { 297 | box-shadow: none; 298 | } 299 | 300 | input[type="text"]:focus, 301 | input[type="password"]:focus, 302 | input[type="email"]:focus, 303 | input[type="tel"]:focus, 304 | select:focus, 305 | textarea:focus { 306 | border-color: #ff7496; 307 | } 308 | 309 | .select-wrapper { 310 | text-decoration: none; 311 | display: block; 312 | position: relative; 313 | } 314 | 315 | .select-wrapper:before { 316 | content: ""; 317 | -moz-osx-font-smoothing: grayscale; 318 | -webkit-font-smoothing: antialiased; 319 | font-family: FontAwesome; 320 | font-style: normal; 321 | font-weight: normal; 322 | text-transform: none !important; 323 | } 324 | 325 | .select-wrapper:before { 326 | color: #c8cccf; 327 | display: block; 328 | height: 2.75em; 329 | line-height: 2.75em; 330 | pointer-events: none; 331 | position: absolute; 332 | right: 0; 333 | text-align: center; 334 | top: 0; 335 | width: 2.75em; 336 | } 337 | 338 | .select-wrapper select::-ms-expand { 339 | display: none; 340 | } 341 | 342 | input[type="text"], 343 | input[type="password"], 344 | input[type="email"], 345 | select { 346 | height: 2.75em; 347 | } 348 | 349 | textarea { 350 | padding: 0.75em 1em; 351 | } 352 | 353 | input[type="checkbox"], 354 | input[type="radio"] { 355 | -moz-appearance: none; 356 | -webkit-appearance: none; 357 | -ms-appearance: none; 358 | appearance: none; 359 | display: block; 360 | float: left; 361 | margin-right: -2em; 362 | opacity: 0; 363 | width: 1em; 364 | z-index: -1; 365 | } 366 | 367 | input[type="checkbox"] + label, 368 | input[type="radio"] + label { 369 | text-decoration: none; 370 | color: #414f57; 371 | cursor: pointer; 372 | display: inline-block; 373 | font-size: 1em; 374 | font-weight: 300; 375 | padding-left: 2.4em; 376 | padding-right: 0.75em; 377 | position: relative; 378 | } 379 | 380 | input[type="checkbox"] + label:before, 381 | input[type="radio"] + label:before { 382 | -moz-osx-font-smoothing: grayscale; 383 | -webkit-font-smoothing: antialiased; 384 | font-family: FontAwesome; 385 | font-style: normal; 386 | font-weight: normal; 387 | text-transform: none !important; 388 | } 389 | 390 | input[type="checkbox"] + label:before, 391 | input[type="radio"] + label:before { 392 | border-radius: 4px; 393 | border: solid 1px #c8cccf; 394 | content: ''; 395 | display: inline-block; 396 | height: 1.65em; 397 | left: 0; 398 | line-height: 1.58125em; 399 | position: absolute; 400 | text-align: center; 401 | top: 0.15em; 402 | width: 1.65em; 403 | } 404 | 405 | input[type="checkbox"]:checked + label:before, 406 | input[type="radio"]:checked + label:before { 407 | color: #ff7496; 408 | content: '\f00c'; 409 | } 410 | 411 | input[type="checkbox"]:focus + label:before, 412 | input[type="radio"]:focus + label:before { 413 | border-color: #ff7496; 414 | } 415 | 416 | input[type="checkbox"] + label:before { 417 | border-radius: 4px; 418 | } 419 | 420 | input[type="radio"] + label:before { 421 | border-radius: 100%; 422 | } 423 | 424 | ::-webkit-input-placeholder { 425 | color: #616f77 !important; 426 | opacity: 1.0; 427 | } 428 | 429 | :-moz-placeholder { 430 | color: #616f77 !important; 431 | opacity: 1.0; 432 | } 433 | 434 | ::-moz-placeholder { 435 | color: #616f77 !important; 436 | opacity: 1.0; 437 | } 438 | 439 | :-ms-input-placeholder { 440 | color: #616f77 !important; 441 | opacity: 1.0; 442 | } 443 | 444 | .formerize-placeholder { 445 | color: #616f77 !important; 446 | opacity: 1.0; 447 | } 448 | 449 | /* Icon */ 450 | 451 | .icon { 452 | text-decoration: none; 453 | position: relative; 454 | border-bottom: none; 455 | } 456 | 457 | .icon:before { 458 | -moz-osx-font-smoothing: grayscale; 459 | -webkit-font-smoothing: antialiased; 460 | font-family: FontAwesome; 461 | font-style: normal; 462 | font-weight: normal; 463 | text-transform: none !important; 464 | } 465 | 466 | .icon > .label { 467 | display: none; 468 | } 469 | 470 | /* List */ 471 | 472 | ol { 473 | list-style: decimal; 474 | margin: 0 0 1.5em 0; 475 | padding-left: 1.25em; 476 | } 477 | 478 | ol li { 479 | padding-left: 0.25em; 480 | } 481 | 482 | ul { 483 | list-style: disc; 484 | margin: 0 0 1.5em 0; 485 | padding-left: 1em; 486 | } 487 | 488 | ul li { 489 | padding-left: 0.5em; 490 | } 491 | 492 | ul.alt { 493 | list-style: none; 494 | padding-left: 0; 495 | } 496 | 497 | ul.alt li { 498 | border-top: solid 1px #c8cccf; 499 | padding: 0.5em 0; 500 | } 501 | 502 | ul.alt li:first-child { 503 | border-top: 0; 504 | padding-top: 0; 505 | } 506 | 507 | ul.icons { 508 | cursor: default; 509 | list-style: none; 510 | padding-left: 0; 511 | margin-top: -0.675em; 512 | } 513 | 514 | ul.icons li { 515 | display: inline-block; 516 | padding: 0.675em 0.5em; 517 | } 518 | 519 | ul.icons li a { 520 | text-decoration: none; 521 | position: relative; 522 | display: block; 523 | width: 3.75em; 524 | height: 3.75em; 525 | border-radius: 100%; 526 | border: solid 1px #c8cccf; 527 | line-height: 3.75em; 528 | overflow: hidden; 529 | text-align: center; 530 | text-indent: 3.75em; 531 | white-space: nowrap; 532 | } 533 | 534 | ul.icons li a:before { 535 | -moz-osx-font-smoothing: grayscale; 536 | -webkit-font-smoothing: antialiased; 537 | font-family: FontAwesome; 538 | font-style: normal; 539 | font-weight: normal; 540 | text-transform: none !important; 541 | } 542 | 543 | ul.icons li a:before { 544 | color: #ffffff; 545 | text-shadow: 1.25px 0px 0px #c8cccf, -1.25px 0px 0px #c8cccf, 0px 1.25px 0px #c8cccf, 0px -1.25px 0px #c8cccf; 546 | } 547 | 548 | ul.icons li a:hover:before { 549 | text-shadow: 1.25px 0px 0px #ff7496, -1.25px 0px 0px #ff7496, 0px 1.25px 0px #ff7496, 0px -1.25px 0px #ff7496; 550 | } 551 | 552 | ul.icons li a:before { 553 | position: absolute; 554 | top: 0; 555 | left: 0; 556 | width: inherit; 557 | height: inherit; 558 | font-size: 1.85rem; 559 | line-height: inherit; 560 | text-align: center; 561 | text-indent: 0; 562 | } 563 | 564 | ul.icons li a:hover { 565 | border-color: #ff7496; 566 | } 567 | 568 | @media screen and (max-width: 480px) { 569 | 570 | ul.icons li a:before { 571 | font-size: 1.5rem; 572 | } 573 | 574 | } 575 | 576 | ul.actions { 577 | cursor: default; 578 | list-style: none; 579 | padding-left: 0; 580 | } 581 | 582 | ul.actions li { 583 | display: inline-block; 584 | padding: 0 0.75em 0 0; 585 | vertical-align: middle; 586 | } 587 | 588 | ul.actions li:last-child { 589 | padding-right: 0; 590 | } 591 | 592 | dl { 593 | margin: 0 0 1.5em 0; 594 | } 595 | 596 | dl dt { 597 | display: block; 598 | margin: 0 0 0.75em 0; 599 | } 600 | 601 | dl dd { 602 | margin-left: 1.5em; 603 | } 604 | 605 | /* Button */ 606 | 607 | input[type="submit"], 608 | input[type="reset"], 609 | input[type="button"], 610 | button, 611 | .button { 612 | -moz-appearance: none; 613 | -webkit-appearance: none; 614 | -ms-appearance: none; 615 | appearance: none; 616 | -moz-transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out, color 0.2s ease-in-out; 617 | -webkit-transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out, color 0.2s ease-in-out; 618 | -ms-transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out, color 0.2s ease-in-out; 619 | transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out, color 0.2s ease-in-out; 620 | display: inline-block; 621 | height: 2.75em; 622 | line-height: 2.75em; 623 | padding: 0 1.5em; 624 | background-color: transparent; 625 | border-radius: 4px; 626 | border: solid 1px #c8cccf; 627 | color: #414f57 !important; 628 | cursor: pointer; 629 | text-align: center; 630 | text-decoration: none; 631 | white-space: nowrap; 632 | } 633 | 634 | input[type="submit"]:hover, 635 | input[type="reset"]:hover, 636 | input[type="button"]:hover, 637 | button:hover, 638 | .button:hover { 639 | border-color: #ff7496; 640 | color: #ff7496 !important; 641 | } 642 | 643 | input[type="submit"].icon, 644 | input[type="reset"].icon, 645 | input[type="button"].icon, 646 | button.icon, 647 | .button.icon { 648 | padding-left: 1.35em; 649 | } 650 | 651 | input[type="submit"].icon:before, 652 | input[type="reset"].icon:before, 653 | input[type="button"].icon:before, 654 | button.icon:before, 655 | .button.icon:before { 656 | margin-right: 0.5em; 657 | } 658 | 659 | input[type="submit"].fit, 660 | input[type="reset"].fit, 661 | input[type="button"].fit, 662 | button.fit, 663 | .button.fit { 664 | display: block; 665 | width: 100%; 666 | margin: 0 0 0.75em 0; 667 | } 668 | 669 | input[type="submit"].small, 670 | input[type="reset"].small, 671 | input[type="button"].small, 672 | button.small, 673 | .button.small { 674 | font-size: 0.8em; 675 | } 676 | 677 | input[type="submit"].big, 678 | input[type="reset"].big, 679 | input[type="button"].big, 680 | button.big, 681 | .button.big { 682 | font-size: 1.35em; 683 | } 684 | 685 | input[type="submit"].disabled, input[type="submit"]:disabled, 686 | input[type="reset"].disabled, 687 | input[type="reset"]:disabled, 688 | input[type="button"].disabled, 689 | input[type="button"]:disabled, 690 | button.disabled, 691 | button:disabled, 692 | .button.disabled, 693 | .button:disabled { 694 | -moz-pointer-events: none; 695 | -webkit-pointer-events: none; 696 | -ms-pointer-events: none; 697 | pointer-events: none; 698 | opacity: 0.5; 699 | } 700 | 701 | /* Main */ 702 | 703 | #main { 704 | position: relative; 705 | max-width: 100%; 706 | min-width: 35em; /* HERE IS THE WIDTH OF THE MAIN BLOCK */ 707 | padding: 4.5em 3em 3em 3em ; 708 | background: #ffffff; 709 | border-radius: 4px; 710 | cursor: default; 711 | opacity: 0.95; 712 | text-align: center; 713 | -moz-transform-origin: 50% 50%; 714 | -webkit-transform-origin: 50% 50%; 715 | -ms-transform-origin: 50% 50%; 716 | transform-origin: 50% 50%; 717 | -moz-transform: rotateX(0deg); 718 | -webkit-transform: rotateX(0deg); 719 | -ms-transform: rotateX(0deg); 720 | transform: rotateX(0deg); 721 | -moz-transition: opacity 1s ease, -moz-transform 1s ease; 722 | -webkit-transition: opacity 1s ease, -webkit-transform 1s ease; 723 | -ms-transition: opacity 1s ease, -ms-transform 1s ease; 724 | transition: opacity 1s ease, transform 1s ease; 725 | } 726 | 727 | #main .avatar { 728 | position: relative; 729 | display: block; 730 | margin-bottom: 1.5em; 731 | } 732 | 733 | #main .avatar img { 734 | display: block; 735 | margin: 0 auto; 736 | border-radius: 100%; 737 | box-shadow: 0 0 0 1.5em #ffffff; 738 | } 739 | 740 | #main .avatar:before { 741 | content: ''; 742 | display: block; 743 | position: absolute; 744 | top: 50%; 745 | left: -3em; 746 | width: calc(100% + 6em); 747 | height: 1px; 748 | z-index: -1; 749 | background: #c8cccf; 750 | } 751 | 752 | @media screen and (max-width: 480px) { 753 | 754 | #main { 755 | min-width: 0; 756 | width: 100%; 757 | padding: 4em 2em 2.5em 2em ; 758 | } 759 | 760 | #main .avatar:before { 761 | left: -2em; 762 | width: calc(100% + 4em); 763 | } 764 | 765 | } 766 | 767 | body.is-loading #main { 768 | opacity: 0; 769 | -moz-transform: rotateX(15deg); 770 | -webkit-transform: rotateX(15deg); 771 | -ms-transform: rotateX(15deg); 772 | transform: rotateX(15deg); 773 | } 774 | 775 | #chat_zone { 776 | font-size: 0.9em; 777 | } 778 | 779 | .question { 780 | text-align: right; 781 | margin: 0 0 0.1em 0; 782 | } 783 | 784 | .answer { 785 | text-align: left; 786 | margin: 0 0 0.3em 0; 787 | } 788 | 789 | /* Footer */ 790 | 791 | #footer { 792 | -moz-align-self: -moz-flex-end; 793 | -webkit-align-self: -webkit-flex-end; 794 | -ms-align-self: -ms-flex-end; 795 | align-self: flex-end; 796 | width: 100%; 797 | padding: 1.5em 0 0 0; 798 | color: rgba(255, 255, 255, 0.75); 799 | cursor: default; 800 | text-align: center; 801 | } 802 | 803 | #footer .copyright { 804 | margin: 0; 805 | padding: 0; 806 | font-size: 0.9em; 807 | list-style: none; 808 | } 809 | 810 | #footer .copyright li { 811 | display: inline-block; 812 | margin: 0 0 0 0.45em; 813 | padding: 0 0 0 0.85em; 814 | border-left: solid 1px rgba(255, 255, 255, 0.5); 815 | line-height: 1; 816 | } 817 | 818 | #footer .copyright li:first-child { 819 | border-left: 0; 820 | } 821 | 822 | /* Wrapper */ 823 | 824 | #wrapper { 825 | display: -moz-flex; 826 | display: -webkit-flex; 827 | display: -ms-flex; 828 | display: flex; 829 | -moz-align-items: center; 830 | -webkit-align-items: center; 831 | -ms-align-items: center; 832 | align-items: center; 833 | -moz-justify-content: space-between; 834 | -webkit-justify-content: space-between; 835 | -ms-justify-content: space-between; 836 | justify-content: space-between; 837 | -moz-flex-direction: column; 838 | -webkit-flex-direction: column; 839 | -ms-flex-direction: column; 840 | flex-direction: column; 841 | -moz-perspective: 1000px; 842 | -webkit-perspective: 1000px; 843 | -ms-perspective: 1000px; 844 | perspective: 1000px; 845 | position: relative; 846 | min-height: 100%; 847 | padding: 1.5em; 848 | z-index: 2; 849 | } 850 | 851 | #wrapper > * { 852 | z-index: 1; 853 | } 854 | 855 | #wrapper:before { 856 | content: ''; 857 | display: block; 858 | } 859 | 860 | @media screen and (max-width: 360px) { 861 | 862 | #wrapper { 863 | padding: 0.75em; 864 | } 865 | 866 | } 867 | 868 | body.is-ie #wrapper { 869 | height: 100%; 870 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/css/noscript.css: -------------------------------------------------------------------------------- 1 | /* 2 | Identity by HTML5 UP 3 | html5up.net | @ajlkn 4 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | */ 6 | 7 | /* Basic */ 8 | 9 | body:after { 10 | display: none; 11 | } 12 | 13 | /* Main */ 14 | 15 | #main { 16 | -moz-transform: none !important; 17 | -webkit-transform: none !important; 18 | -ms-transform: none !important; 19 | transform: none !important; 20 | opacity: 1 !important; 21 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conchylicultor/DeepQA/886ec77ac49ff70be1b6d4de30fffe6a96480a4f/chatbot_website/chatbot_interface/static/assets/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conchylicultor/DeepQA/886ec77ac49ff70be1b6d4de30fffe6a96480a4f/chatbot_website/chatbot_interface/static/assets/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conchylicultor/DeepQA/886ec77ac49ff70be1b6d4de30fffe6a96480a4f/chatbot_website/chatbot_interface/static/assets/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conchylicultor/DeepQA/886ec77ac49ff70be1b6d4de30fffe6a96480a4f/chatbot_website/chatbot_interface/static/assets/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conchylicultor/DeepQA/886ec77ac49ff70be1b6d4de30fffe6a96480a4f/chatbot_website/chatbot_interface/static/assets/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/js/html5shiv.js: -------------------------------------------------------------------------------- 1 | /* 2 | HTML5 Shiv v3.6.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); 5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; 6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| 7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup main mark meter nav output progress section summary time video",version:"3.6.2",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment(); 8 | for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d #mq-test-1 { width: 42px; }',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){v(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},g=function(a){return a.replace(c.regex.minmaxwh,"").match(c.regex.other)};if(c.ajax=f,c.queue=d,c.unsupportedmq=g,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,comments:/\/\*[^*]*\*+([^/][^*]*\*+)*\//gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,maxw:/\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,minmaxwh:/\(\s*m(in|ax)\-(height|width)\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/gi,other:/\([^\)]*\)/g},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var h,i,j,k=a.document,l=k.documentElement,m=[],n=[],o=[],p={},q=30,r=k.getElementsByTagName("head")[0]||l,s=k.getElementsByTagName("base")[0],t=r.getElementsByTagName("link"),u=function(){var a,b=k.createElement("div"),c=k.body,d=l.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=k.createElement("body"),c.style.background="none"),l.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&l.insertBefore(c,l.firstChild),a=b.offsetWidth,f?l.removeChild(c):c.removeChild(b),l.style.fontSize=d,e&&(c.style.fontSize=e),a=j=parseFloat(a)},v=function(b){var c="clientWidth",d=l[c],e="CSS1Compat"===k.compatMode&&d||k.body[c]||d,f={},g=t[t.length-1],p=(new Date).getTime();if(b&&h&&q>p-h)return a.clearTimeout(i),i=a.setTimeout(v,q),void 0;h=p;for(var s in m)if(m.hasOwnProperty(s)){var w=m[s],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?j||u():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?j||u():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(n[w.rules]))}for(var C in o)o.hasOwnProperty(C)&&o[C]&&o[C].parentNode===r&&r.removeChild(o[C]);o.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=k.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,r.insertBefore(E,g.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(k.createTextNode(F)),o.push(E)}},w=function(a,b,d){var e=a.replace(c.regex.comments,"").replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},i=!f&&d;b.length&&(b+="/"),i&&(f=1);for(var j=0;f>j;j++){var k,l,o,p;i?(k=d,n.push(h(a))):(k=e[j].match(c.regex.findStyles)&&RegExp.$1,n.push(RegExp.$2&&h(RegExp.$2))),o=k.split(","),p=o.length;for(var q=0;p>q;q++)l=o[q],g(l)||m.push({media:l.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:n.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}v()},x=function(){if(d.length){var b=d.shift();f(b.href,function(c){w(c,b.href,b.media),p[b.href]=!0,a.setTimeout(function(){x()},0)})}},y=function(){for(var b=0;b=320px. 14 | @include breakpoint(xsmall) { 15 | html, body { 16 | min-width: 320px; 17 | } 18 | } 19 | 20 | body { 21 | 22 | // Prevents animation/transition "flicker" on page load. 23 | // Automatically added/removed by js/main.js. 24 | &.is-loading { 25 | *, *:before, *:after { 26 | @include vendor('animation', 'none !important'); 27 | @include vendor('transition', 'none !important'); 28 | } 29 | } 30 | 31 | } 32 | 33 | html { 34 | height: 100%; 35 | } 36 | 37 | body { 38 | height: 100%; 39 | background-color: _palette(bg); 40 | @include vendor('background-image', ( 41 | 'url("images/overlay.png")', 42 | 'linear-gradient(60deg, #{transparentize(_palette(accent1), 0.5)} 5%, #{transparentize(_palette(accent2), 0.65)})', 43 | 'url("../../images/bg.jpg")' 44 | )); 45 | background-repeat: repeat, no-repeat, no-repeat; 46 | background-size: 100px 100px, cover, cover; 47 | background-position: top left, center center, bottom center; 48 | background-attachment: fixed, fixed, fixed; 49 | 50 | &:after { 51 | content: ''; 52 | display: block; 53 | position: fixed; 54 | top: 0; 55 | left: 0; 56 | width: 100%; 57 | height: inherit; 58 | opacity: 0; 59 | z-index: 1; 60 | 61 | background-color: _palette(bg); 62 | @include vendor('background-image', ( 63 | 'url("images/overlay.png")', 64 | 'linear-gradient(60deg, #{transparentize(_palette(accent1), 0.5)} 5%, #{transparentize(_palette(accent2), 0.65)})' 65 | )); 66 | background-repeat: repeat, no-repeat; 67 | background-size: 100px 100px, cover; 68 | background-position: top left, center center; 69 | 70 | @include vendor('transition', 'opacity #{_duration(bg)} ease-out'); 71 | } 72 | 73 | &.is-loading { 74 | &:after { 75 | opacity: 1; 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/sass/base/_typography.scss: -------------------------------------------------------------------------------- 1 | @import '../libs/vars'; 2 | @import '../libs/functions'; 3 | @import '../libs/mixins'; 4 | 5 | /// 6 | /// Identity by HTML5 UP 7 | /// html5up.net | @ajlkn 8 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 9 | /// 10 | 11 | /* Type */ 12 | 13 | body, input, select, textarea { 14 | color: _palette(fg); 15 | font-family: _font(family); 16 | font-size: 14pt; 17 | font-weight: _font(weight); 18 | line-height: 2; 19 | letter-spacing: _size(letter-spacing); 20 | text-transform: uppercase; 21 | 22 | @include breakpoint(xlarge) { 23 | font-size: 11pt; 24 | } 25 | 26 | @include breakpoint(xsmall) { 27 | font-size: 10pt; 28 | line-height: 1.75; 29 | } 30 | } 31 | 32 | a { 33 | @include vendor('transition', ( 34 | 'color #{_duration(transition)} ease', 35 | 'border-color #{_duration(transition)} ease' 36 | )); 37 | color: inherit; 38 | text-decoration: none; 39 | 40 | &:before { 41 | @include vendor('transition', ( 42 | 'color #{_duration(transition)} ease', 43 | 'text-shadow #{_duration(transition)} ease' 44 | )); 45 | } 46 | 47 | &:hover { 48 | color: _palette(highlight); 49 | } 50 | } 51 | 52 | strong, b { 53 | color: _palette(fg-bold); 54 | } 55 | 56 | em, i { 57 | font-style: italic; 58 | } 59 | 60 | p { 61 | margin: 0 0 _size(element-margin) 0; 62 | } 63 | 64 | h1, h2, h3, h4, h5, h6 { 65 | color: _palette(fg-bold); 66 | line-height: 1.5; 67 | margin: 0 0 (_size(element-margin) * 0.5) 0; 68 | 69 | a { 70 | color: inherit; 71 | text-decoration: none; 72 | } 73 | } 74 | 75 | h1 { 76 | font-size: 1.85em; 77 | letter-spacing: (_size(letter-spacing) * 1.1); 78 | margin: 0 0 (_size(element-margin) * 0.35) 0; 79 | } 80 | 81 | h2 { 82 | font-size: 1.25em; 83 | } 84 | 85 | h3 { 86 | font-size: 1em; 87 | } 88 | 89 | h4 { 90 | font-size: 1em; 91 | } 92 | 93 | h5 { 94 | font-size: 1em; 95 | } 96 | 97 | h6 { 98 | font-size: 1em; 99 | } 100 | 101 | @include breakpoint(xsmall) { 102 | h1 { 103 | font-size: 1.65em; 104 | } 105 | } 106 | 107 | sub { 108 | font-size: 0.8em; 109 | position: relative; 110 | top: 0.5em; 111 | } 112 | 113 | sup { 114 | font-size: 0.8em; 115 | position: relative; 116 | top: -0.5em; 117 | } 118 | 119 | hr { 120 | border: 0; 121 | border-bottom: solid _size(border-width) _palette(border); 122 | margin: (_size(element-margin) * 2) 0; 123 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/sass/components/_button.scss: -------------------------------------------------------------------------------- 1 | @import '../libs/vars'; 2 | @import '../libs/functions'; 3 | @import '../libs/mixins'; 4 | 5 | /// 6 | /// Identity by HTML5 UP 7 | /// html5up.net | @ajlkn 8 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 9 | /// 10 | 11 | /* Button */ 12 | 13 | input[type="submit"], 14 | input[type="reset"], 15 | input[type="button"], 16 | button, 17 | .button { 18 | @include vendor('appearance', 'none'); 19 | @include vendor('transition', ( 20 | 'background-color #{_duration(transition)} ease-in-out', 21 | 'border-color #{_duration(transition)} ease-in-out', 22 | 'color #{_duration(transition)} ease-in-out' 23 | )); 24 | display: inline-block; 25 | height: _size(element-height); 26 | line-height: _size(element-height); 27 | padding: 0 1.5em; 28 | background-color: transparent; 29 | border-radius: _size(border-radius); 30 | border: solid 1px _palette(border); 31 | color: _palette(fg) !important; 32 | cursor: pointer; 33 | text-align: center; 34 | text-decoration: none; 35 | white-space: nowrap; 36 | 37 | &:hover { 38 | border-color: _palette(highlight); 39 | color: _palette(highlight) !important; 40 | } 41 | 42 | &.icon { 43 | padding-left: 1.35em; 44 | 45 | &:before { 46 | margin-right: 0.5em; 47 | } 48 | } 49 | 50 | &.fit { 51 | display: block; 52 | width: 100%; 53 | margin: 0 0 (_size(element-margin) * 0.5) 0; 54 | } 55 | 56 | &.small { 57 | font-size: 0.8em; 58 | } 59 | 60 | &.big { 61 | font-size: 1.35em; 62 | } 63 | 64 | &.disabled, 65 | &:disabled { 66 | @include vendor('pointer-events', 'none'); 67 | opacity: 0.5; 68 | } 69 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/sass/components/_form.scss: -------------------------------------------------------------------------------- 1 | @import '../libs/vars'; 2 | @import '../libs/functions'; 3 | @import '../libs/mixins'; 4 | 5 | /// 6 | /// Identity by HTML5 UP 7 | /// html5up.net | @ajlkn 8 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 9 | /// 10 | 11 | /* Form */ 12 | 13 | form { 14 | margin: 0 0 _size(element-margin) 0; 15 | 16 | > .field { 17 | margin: 0 0 _size(element-margin) 0; 18 | 19 | > :last-child { 20 | margin-bottom: 0; 21 | } 22 | } 23 | } 24 | 25 | label { 26 | color: _palette(fg-bold); 27 | display: block; 28 | font-size: 0.9em; 29 | margin: 0 0 (_size(element-margin) * 0.5) 0; 30 | } 31 | 32 | input[type="text"], 33 | input[type="password"], 34 | input[type="email"], 35 | input[type="tel"], 36 | select, 37 | textarea { 38 | @include vendor('appearance', 'none'); 39 | border-radius: _size(border-radius); 40 | border: solid 1px _palette(border); 41 | color: inherit; 42 | display: block; 43 | outline: 0; 44 | padding: 0 1em; 45 | text-decoration: none; 46 | width: 100%; 47 | 48 | &:invalid { 49 | box-shadow: none; 50 | } 51 | 52 | &:focus { 53 | border-color: _palette(highlight); 54 | } 55 | } 56 | 57 | .select-wrapper { 58 | @include icon('\f078'); 59 | display: block; 60 | position: relative; 61 | 62 | &:before { 63 | color: _palette(border); 64 | display: block; 65 | height: _size(element-height); 66 | line-height: _size(element-height); 67 | pointer-events: none; 68 | position: absolute; 69 | right: 0; 70 | text-align: center; 71 | top: 0; 72 | width: _size(element-height); 73 | } 74 | 75 | select::-ms-expand { 76 | display: none; 77 | } 78 | } 79 | 80 | input[type="text"], 81 | input[type="password"], 82 | input[type="email"], 83 | select { 84 | height: _size(element-height); 85 | } 86 | 87 | textarea { 88 | padding: 0.75em 1em; 89 | } 90 | 91 | input[type="checkbox"], 92 | input[type="radio"], { 93 | @include vendor('appearance', 'none'); 94 | display: block; 95 | float: left; 96 | margin-right: -2em; 97 | opacity: 0; 98 | width: 1em; 99 | z-index: -1; 100 | 101 | & + label { 102 | @include icon; 103 | color: _palette(fg); 104 | cursor: pointer; 105 | display: inline-block; 106 | font-size: 1em; 107 | font-weight: _font(weight); 108 | padding-left: (_size(element-height) * 0.6) + 0.75em; 109 | padding-right: 0.75em; 110 | position: relative; 111 | 112 | &:before { 113 | background: _palette(border-bg); 114 | border-radius: _size(border-radius); 115 | border: solid 1px _palette(border); 116 | content: ''; 117 | display: inline-block; 118 | height: (_size(element-height) * 0.6); 119 | left: 0; 120 | line-height: (_size(element-height) * 0.575); 121 | position: absolute; 122 | text-align: center; 123 | top: 0.15em; 124 | width: (_size(element-height) * 0.6); 125 | } 126 | } 127 | 128 | &:checked + label { 129 | &:before { 130 | color: _palette(highlight); 131 | content: '\f00c'; 132 | } 133 | } 134 | 135 | &:focus + label { 136 | &:before { 137 | border-color: _palette(highlight); 138 | } 139 | } 140 | } 141 | 142 | input[type="checkbox"] { 143 | & + label { 144 | &:before { 145 | border-radius: _size(border-radius); 146 | } 147 | } 148 | } 149 | 150 | input[type="radio"] { 151 | & + label { 152 | &:before { 153 | border-radius: 100%; 154 | } 155 | } 156 | } 157 | 158 | ::-webkit-input-placeholder { 159 | color: _palette(fg-light) !important; 160 | opacity: 1.0; 161 | } 162 | 163 | :-moz-placeholder { 164 | color: _palette(fg-light) !important; 165 | opacity: 1.0; 166 | } 167 | 168 | ::-moz-placeholder { 169 | color: _palette(fg-light) !important; 170 | opacity: 1.0; 171 | } 172 | 173 | :-ms-input-placeholder { 174 | color: _palette(fg-light) !important; 175 | opacity: 1.0; 176 | } 177 | 178 | .formerize-placeholder { 179 | color: _palette(fg-light) !important; 180 | opacity: 1.0; 181 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/sass/components/_icon.scss: -------------------------------------------------------------------------------- 1 | @import '../libs/vars'; 2 | @import '../libs/functions'; 3 | @import '../libs/mixins'; 4 | 5 | /// 6 | /// Identity by HTML5 UP 7 | /// html5up.net | @ajlkn 8 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 9 | /// 10 | 11 | /* Icon */ 12 | 13 | .icon { 14 | @include icon; 15 | position: relative; 16 | border-bottom: none; 17 | 18 | > .label { 19 | display: none; 20 | } 21 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/sass/components/_list.scss: -------------------------------------------------------------------------------- 1 | @import '../libs/vars'; 2 | @import '../libs/functions'; 3 | @import '../libs/mixins'; 4 | 5 | /// 6 | /// Identity by HTML5 UP 7 | /// html5up.net | @ajlkn 8 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 9 | /// 10 | 11 | /* List */ 12 | 13 | ol { 14 | list-style: decimal; 15 | margin: 0 0 _size(element-margin) 0; 16 | padding-left: 1.25em; 17 | 18 | li { 19 | padding-left: 0.25em; 20 | } 21 | } 22 | 23 | ul { 24 | list-style: disc; 25 | margin: 0 0 _size(element-margin) 0; 26 | padding-left: 1em; 27 | 28 | li { 29 | padding-left: 0.5em; 30 | } 31 | 32 | &.alt { 33 | list-style: none; 34 | padding-left: 0; 35 | 36 | li { 37 | border-top: solid 1px _palette(border); 38 | padding: 0.5em 0; 39 | 40 | &:first-child { 41 | border-top: 0; 42 | padding-top: 0; 43 | } 44 | } 45 | } 46 | 47 | &.icons { 48 | cursor: default; 49 | list-style: none; 50 | padding-left: 0; 51 | margin-top: -0.675em; 52 | 53 | li { 54 | display: inline-block; 55 | padding: 0.675em 0.5em; 56 | 57 | a { 58 | @include icon-alt(false, true); 59 | position: relative; 60 | display: block; 61 | width: 3.75em; 62 | height: 3.75em; 63 | border-radius: 100%; 64 | border: solid _size(border-width) _palette(border); 65 | line-height: 3.75em; 66 | overflow: hidden; 67 | text-align: center; 68 | text-indent: 3.75em; 69 | white-space: nowrap; 70 | 71 | &:before { 72 | position: absolute; 73 | top: 0; 74 | left: 0; 75 | width: inherit; 76 | height: inherit; 77 | font-size: 1.85rem; 78 | line-height: inherit; 79 | text-align: center; 80 | text-indent: 0; 81 | } 82 | 83 | &:hover { 84 | border-color: _palette(highlight); 85 | } 86 | } 87 | } 88 | 89 | @include breakpoint(xsmall) { 90 | li { 91 | a { 92 | &:before { 93 | font-size: 1.5rem; 94 | } 95 | } 96 | } 97 | } 98 | } 99 | 100 | &.actions { 101 | cursor: default; 102 | list-style: none; 103 | padding-left: 0; 104 | 105 | li { 106 | display: inline-block; 107 | padding: 0 (_size(element-margin) * 0.5) 0 0; 108 | vertical-align: middle; 109 | 110 | &:last-child { 111 | padding-right: 0; 112 | } 113 | } 114 | } 115 | } 116 | 117 | dl { 118 | margin: 0 0 _size(element-margin) 0; 119 | 120 | dt { 121 | display: block; 122 | font-weight: _font(weight-bold); 123 | margin: 0 0 (_size(element-margin) * 0.5) 0; 124 | } 125 | 126 | dd { 127 | margin-left: _size(element-margin); 128 | } 129 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/sass/ie8.scss: -------------------------------------------------------------------------------- 1 | @import 'libs/vars'; 2 | @import 'libs/functions'; 3 | @import 'libs/mixins'; 4 | @import 'libs/skel'; 5 | 6 | /* 7 | Identity by HTML5 UP 8 | html5up.net | @ajlkn 9 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 10 | */ 11 | 12 | /* List */ 13 | 14 | ul { 15 | &.icons { 16 | li { 17 | a { 18 | -ms-behavior: url('assets/js/PIE.htc'); 19 | 20 | &:before { 21 | text-align: center; 22 | font-size: 26px; 23 | } 24 | } 25 | } 26 | } 27 | } 28 | 29 | /* Main */ 30 | 31 | #main { 32 | -ms-behavior: url('assets/js/PIE.htc'); 33 | 34 | .avatar { 35 | img { 36 | -ms-behavior: url('assets/js/PIE.htc'); 37 | } 38 | } 39 | } 40 | 41 | /* Footer */ 42 | 43 | #footer { 44 | color: #fff; 45 | 46 | .copyright { 47 | li { 48 | border-left: solid 1px #fff; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/sass/ie9.scss: -------------------------------------------------------------------------------- 1 | @import 'libs/vars'; 2 | @import 'libs/functions'; 3 | @import 'libs/mixins'; 4 | @import 'libs/skel'; 5 | 6 | /* 7 | Identity by HTML5 UP 8 | html5up.net | @ajlkn 9 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 10 | */ 11 | 12 | /* Basic */ 13 | 14 | body { 15 | background-image: url('../../images/bg.jpg'); 16 | background-repeat: no-repeat; 17 | background-size: cover; 18 | background-position: bottom center; 19 | background-attachment: fixed; 20 | 21 | &:after { 22 | display: none; 23 | } 24 | } 25 | 26 | /* List */ 27 | 28 | ul { 29 | &.icons { 30 | li { 31 | a { 32 | &:before { 33 | color: _palette(border); 34 | } 35 | 36 | &:hover { 37 | &:before { 38 | color: _palette(highlight); 39 | } 40 | } 41 | } 42 | } 43 | } 44 | } 45 | 46 | /* Wrapper */ 47 | 48 | #wrapper { 49 | text-align: center; 50 | } 51 | 52 | /* Main */ 53 | 54 | #main { 55 | display: inline-block; 56 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/sass/layout/_footer.scss: -------------------------------------------------------------------------------- 1 | @import '../libs/vars'; 2 | @import '../libs/functions'; 3 | @import '../libs/mixins'; 4 | 5 | /// 6 | /// Identity by HTML5 UP 7 | /// html5up.net | @ajlkn 8 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 9 | /// 10 | 11 | /* Footer */ 12 | 13 | #footer { 14 | @include vendor('align-self', 'flex-end'); 15 | width: 100%; 16 | padding: _size(element-margin) 0 0 0; 17 | color: rgba(255,255,255,0.75); 18 | cursor: default; 19 | text-align: center; 20 | 21 | .copyright { 22 | margin: 0; 23 | padding: 0; 24 | font-size: 0.9em; 25 | list-style: none; 26 | 27 | li { 28 | display: inline-block; 29 | margin: 0 0 0 (0.85em - (_size(letter-spacing) * 2)); 30 | padding: 0 0 0 0.85em; 31 | border-left: solid _size(border-width) rgba(255,255,255,0.5); 32 | line-height: 1; 33 | 34 | &:first-child { 35 | border-left: 0; 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/sass/layout/_main.scss: -------------------------------------------------------------------------------- 1 | @import '../libs/vars'; 2 | @import '../libs/functions'; 3 | @import '../libs/mixins'; 4 | 5 | /// 6 | /// Identity by HTML5 UP 7 | /// html5up.net | @ajlkn 8 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 9 | /// 10 | 11 | /* Main */ 12 | 13 | #main { 14 | position: relative; 15 | max-width: 100%; 16 | min-width: 27em; 17 | @include padding(4.5em, 3em); 18 | 19 | background: _palette(bg); 20 | border-radius: _size(border-radius); 21 | cursor: default; 22 | opacity: 0.95; 23 | text-align: center; 24 | 25 | @include vendor('transform-origin', '50% 50%'); 26 | @include vendor('transform', 'rotateX(0deg)'); 27 | @include vendor('transition', ( 28 | 'opacity #{_duration(main)} ease', 29 | 'transform #{_duration(main)} ease' 30 | )); 31 | 32 | .avatar { 33 | position: relative; 34 | display: block; 35 | margin-bottom: _size(element-margin); 36 | 37 | img { 38 | display: block; 39 | margin: 0 auto; 40 | border-radius: 100%; 41 | box-shadow: 0 0 0 1.5em _palette(bg); 42 | } 43 | 44 | &:before { 45 | content: ''; 46 | display: block; 47 | position: absolute; 48 | top: 50%; 49 | left: -3em; 50 | width: calc(100% + 6em); 51 | height: _size(border-width); 52 | z-index: -1; 53 | background: _palette(border); 54 | } 55 | } 56 | 57 | @include breakpoint(xsmall) { 58 | min-width: 0; 59 | width: 100%; 60 | @include padding(4em, 2em); 61 | 62 | .avatar { 63 | &:before { 64 | left: -2em; 65 | width: calc(100% + 4em); 66 | } 67 | } 68 | } 69 | 70 | body.is-loading & { 71 | opacity: 0; 72 | @include vendor('transform', 'rotateX(15deg)'); 73 | } 74 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/sass/layout/_wrapper.scss: -------------------------------------------------------------------------------- 1 | @import '../libs/vars'; 2 | @import '../libs/functions'; 3 | @import '../libs/mixins'; 4 | 5 | /// 6 | /// Identity by HTML5 UP 7 | /// html5up.net | @ajlkn 8 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 9 | /// 10 | 11 | /* Wrapper */ 12 | 13 | #wrapper { 14 | @include vendor('display', 'flex'); 15 | @include vendor('align-items', 'center'); 16 | @include vendor('justify-content', 'space-between'); 17 | @include vendor('flex-direction', 'column'); 18 | @include vendor('perspective', '1000px'); 19 | position: relative; 20 | min-height: 100%; 21 | padding: _size(element-margin); 22 | z-index: 2; 23 | 24 | > * { 25 | z-index: 1; 26 | } 27 | 28 | &:before { 29 | content: ''; 30 | display: block; 31 | } 32 | 33 | @include breakpoint(xxsmall) { 34 | padding: (_size(element-margin) * 0.5); 35 | } 36 | 37 | body.is-ie & { 38 | height: 100%; 39 | } 40 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/sass/libs/_functions.scss: -------------------------------------------------------------------------------- 1 | /// Gets a duration value. 2 | /// @param {string} $keys Key(s). 3 | /// @return {string} Value. 4 | @function _duration($keys...) { 5 | @return val($duration, $keys...); 6 | } 7 | 8 | /// Gets a font value. 9 | /// @param {string} $keys Key(s). 10 | /// @return {string} Value. 11 | @function _font($keys...) { 12 | @return val($font, $keys...); 13 | } 14 | 15 | /// Gets a misc value. 16 | /// @param {string} $keys Key(s). 17 | /// @return {string} Value. 18 | @function _misc($keys...) { 19 | @return val($misc, $keys...); 20 | } 21 | 22 | /// Gets a palette value. 23 | /// @param {string} $keys Key(s). 24 | /// @return {string} Value. 25 | @function _palette($keys...) { 26 | @return val($palette, $keys...); 27 | } 28 | 29 | /// Gets a size value. 30 | /// @param {string} $keys Key(s). 31 | /// @return {string} Value. 32 | @function _size($keys...) { 33 | @return val($size, $keys...); 34 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/sass/libs/_mixins.scss: -------------------------------------------------------------------------------- 1 | /// Makes an element's :before pseudoelement a FontAwesome icon. 2 | /// @param {string} $content Optional content value to use. 3 | /// @param {string} $where Optional pseudoelement to target (before or after). 4 | @mixin icon($content: false, $where: before) { 5 | 6 | text-decoration: none; 7 | 8 | &:#{$where} { 9 | 10 | @if $content { 11 | content: $content; 12 | } 13 | 14 | -moz-osx-font-smoothing: grayscale; 15 | -webkit-font-smoothing: antialiased; 16 | font-family: FontAwesome; 17 | font-style: normal; 18 | font-weight: normal; 19 | text-transform: none !important; 20 | 21 | } 22 | 23 | } 24 | 25 | /// Applies padding to an element, taking the current element-margin value into account. 26 | /// @param {mixed} $tb Top/bottom padding. 27 | /// @param {mixed} $lr Left/right padding. 28 | /// @param {list} $pad Optional extra padding (in the following order top, right, bottom, left) 29 | /// @param {bool} $important If true, adds !important. 30 | @mixin padding($tb, $lr, $pad: (0,0,0,0), $important: null) { 31 | 32 | @if $important { 33 | $important: '!important'; 34 | } 35 | 36 | padding: ($tb + nth($pad,1)) ($lr + nth($pad,2)) max(0.1em, $tb - _size(element-margin) + nth($pad,3)) ($lr + nth($pad,4)) #{$important}; 37 | 38 | } 39 | 40 | /// Encodes a SVG data URL so IE doesn't choke (via codepen.io/jakob-e/pen/YXXBrp). 41 | /// @param {string} $svg SVG data URL. 42 | /// @return {string} Encoded SVG data URL. 43 | @function svg-url($svg) { 44 | 45 | $svg: str-replace($svg, '"', '\''); 46 | $svg: str-replace($svg, '<', '%3C'); 47 | $svg: str-replace($svg, '>', '%3E'); 48 | $svg: str-replace($svg, '&', '%26'); 49 | $svg: str-replace($svg, '#', '%23'); 50 | $svg: str-replace($svg, '{', '%7B'); 51 | $svg: str-replace($svg, '}', '%7D'); 52 | $svg: str-replace($svg, ';', '%3B'); 53 | 54 | @return url("data:image/svg+xml;charset=utf8,#{$svg}"); 55 | 56 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/sass/libs/_skel.scss: -------------------------------------------------------------------------------- 1 | // skel.scss v3.0.1 | (c) skel.io | MIT licensed */ 2 | 3 | // Vars. 4 | 5 | /// Breakpoints. 6 | /// @var {list} 7 | $breakpoints: () !global; 8 | 9 | /// Vendor prefixes. 10 | /// @var {list} 11 | $vendor-prefixes: ( 12 | '-moz-', 13 | '-webkit-', 14 | '-ms-', 15 | '' 16 | ); 17 | 18 | /// Properties that should be vendorized. 19 | /// @var {list} 20 | $vendor-properties: ( 21 | 'align-content', 22 | 'align-items', 23 | 'align-self', 24 | 'animation', 25 | 'animation-delay', 26 | 'animation-direction', 27 | 'animation-duration', 28 | 'animation-fill-mode', 29 | 'animation-iteration-count', 30 | 'animation-name', 31 | 'animation-play-state', 32 | 'animation-timing-function', 33 | 'appearance', 34 | 'backface-visibility', 35 | 'box-sizing', 36 | 'filter', 37 | 'flex', 38 | 'flex-basis', 39 | 'flex-direction', 40 | 'flex-flow', 41 | 'flex-grow', 42 | 'flex-shrink', 43 | 'flex-wrap', 44 | 'justify-content', 45 | 'order', 46 | 'perspective', 47 | 'pointer-events', 48 | 'transform', 49 | 'transform-origin', 50 | 'transform-style', 51 | 'transition', 52 | 'transition-delay', 53 | 'transition-duration', 54 | 'transition-property', 55 | 'transition-timing-function', 56 | 'user-select' 57 | ); 58 | 59 | /// Values that should be vendorized. 60 | /// @var {list} 61 | $vendor-values: ( 62 | 'filter', 63 | 'flex', 64 | 'linear-gradient', 65 | 'radial-gradient', 66 | 'transform' 67 | ); 68 | 69 | // Functions. 70 | 71 | /// Removes a specific item from a list. 72 | /// @author Hugo Giraudel 73 | /// @param {list} $list List. 74 | /// @param {integer} $index Index. 75 | /// @return {list} Updated list. 76 | @function remove-nth($list, $index) { 77 | 78 | $result: null; 79 | 80 | @if type-of($index) != number { 81 | @warn "$index: #{quote($index)} is not a number for `remove-nth`."; 82 | } 83 | @else if $index == 0 { 84 | @warn "List index 0 must be a non-zero integer for `remove-nth`."; 85 | } 86 | @else if abs($index) > length($list) { 87 | @warn "List index is #{$index} but list is only #{length($list)} item long for `remove-nth`."; 88 | } 89 | @else { 90 | 91 | $result: (); 92 | $index: if($index < 0, length($list) + $index + 1, $index); 93 | 94 | @for $i from 1 through length($list) { 95 | 96 | @if $i != $index { 97 | $result: append($result, nth($list, $i)); 98 | } 99 | 100 | } 101 | 102 | } 103 | 104 | @return $result; 105 | 106 | } 107 | 108 | /// Replaces a substring within another string. 109 | /// @author Hugo Giraudel 110 | /// @param {string} $string String. 111 | /// @param {string} $search Substring. 112 | /// @param {string} $replace Replacement. 113 | /// @return {string} Updated string. 114 | @function str-replace($string, $search, $replace: '') { 115 | 116 | $index: str-index($string, $search); 117 | 118 | @if $index { 119 | @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace); 120 | } 121 | 122 | @return $string; 123 | 124 | } 125 | 126 | /// Replaces a substring within each string in a list. 127 | /// @param {list} $strings List of strings. 128 | /// @param {string} $search Substring. 129 | /// @param {string} $replace Replacement. 130 | /// @return {list} Updated list of strings. 131 | @function str-replace-all($strings, $search, $replace: '') { 132 | 133 | @each $string in $strings { 134 | $strings: set-nth($strings, index($strings, $string), str-replace($string, $search, $replace)); 135 | } 136 | 137 | @return $strings; 138 | 139 | } 140 | 141 | /// Gets a value from a map. 142 | /// @author Hugo Giraudel 143 | /// @param {map} $map Map. 144 | /// @param {string} $keys Key(s). 145 | /// @return {string} Value. 146 | @function val($map, $keys...) { 147 | 148 | @if nth($keys, 1) == null { 149 | $keys: remove-nth($keys, 1); 150 | } 151 | 152 | @each $key in $keys { 153 | $map: map-get($map, $key); 154 | } 155 | 156 | @return $map; 157 | 158 | } 159 | 160 | // Mixins. 161 | 162 | /// Sets the global box model. 163 | /// @param {string} $model Model (default is content). 164 | @mixin boxModel($model: 'content') { 165 | 166 | $x: $model + '-box'; 167 | 168 | *, *:before, *:after { 169 | -moz-box-sizing: #{$x}; 170 | -webkit-box-sizing: #{$x}; 171 | box-sizing: #{$x}; 172 | } 173 | 174 | } 175 | 176 | /// Wraps @content in a @media block using a given breakpoint. 177 | /// @param {string} $breakpoint Breakpoint. 178 | /// @param {map} $queries Additional queries. 179 | @mixin breakpoint($breakpoint: null, $queries: null) { 180 | 181 | $query: 'screen'; 182 | 183 | // Breakpoint. 184 | @if $breakpoint and map-has-key($breakpoints, $breakpoint) { 185 | $query: $query + ' and ' + map-get($breakpoints, $breakpoint); 186 | } 187 | 188 | // Queries. 189 | @if $queries { 190 | @each $k, $v in $queries { 191 | $query: $query + ' and (' + $k + ':' + $v + ')'; 192 | } 193 | } 194 | 195 | @media #{$query} { 196 | @content; 197 | } 198 | 199 | } 200 | 201 | /// Wraps @content in a @media block targeting a specific orientation. 202 | /// @param {string} $orientation Orientation. 203 | @mixin orientation($orientation) { 204 | @media screen and (orientation: #{$orientation}) { 205 | @content; 206 | } 207 | } 208 | 209 | /// Utility mixin for containers. 210 | /// @param {mixed} $width Width. 211 | @mixin containers($width) { 212 | 213 | // Locked? 214 | $lock: false; 215 | 216 | @if length($width) == 2 { 217 | $width: nth($width, 1); 218 | $lock: true; 219 | } 220 | 221 | // Modifiers. 222 | .container.\31 25\25 { width: 100%; max-width: $width * 1.25; min-width: $width; } 223 | .container.\37 5\25 { width: $width * 0.75; } 224 | .container.\35 0\25 { width: $width * 0.5; } 225 | .container.\32 5\25 { width: $width * 0.25; } 226 | 227 | // Main class. 228 | .container { 229 | @if $lock { 230 | width: $width !important; 231 | } 232 | @else { 233 | width: $width; 234 | } 235 | } 236 | 237 | } 238 | 239 | /// Utility mixin for grid. 240 | /// @param {list} $gutters Column and row gutters (default is 40px). 241 | /// @param {string} $breakpointName Optional breakpoint name. 242 | @mixin grid($gutters: 40px, $breakpointName: null) { 243 | 244 | // Gutters. 245 | @include grid-gutters($gutters); 246 | @include grid-gutters($gutters, \32 00\25, 2); 247 | @include grid-gutters($gutters, \31 50\25, 1.5); 248 | @include grid-gutters($gutters, \35 0\25, 0.5); 249 | @include grid-gutters($gutters, \32 5\25, 0.25); 250 | 251 | // Cells. 252 | $x: ''; 253 | 254 | @if $breakpointName { 255 | $x: '\\28' + $breakpointName + '\\29'; 256 | } 257 | 258 | .\31 2u#{$x}, .\31 2u\24#{$x} { width: 100%; clear: none; margin-left: 0; } 259 | .\31 1u#{$x}, .\31 1u\24#{$x} { width: 91.6666666667%; clear: none; margin-left: 0; } 260 | .\31 0u#{$x}, .\31 0u\24#{$x} { width: 83.3333333333%; clear: none; margin-left: 0; } 261 | .\39 u#{$x}, .\39 u\24#{$x} { width: 75%; clear: none; margin-left: 0; } 262 | .\38 u#{$x}, .\38 u\24#{$x} { width: 66.6666666667%; clear: none; margin-left: 0; } 263 | .\37 u#{$x}, .\37 u\24#{$x} { width: 58.3333333333%; clear: none; margin-left: 0; } 264 | .\36 u#{$x}, .\36 u\24#{$x} { width: 50%; clear: none; margin-left: 0; } 265 | .\35 u#{$x}, .\35 u\24#{$x} { width: 41.6666666667%; clear: none; margin-left: 0; } 266 | .\34 u#{$x}, .\34 u\24#{$x} { width: 33.3333333333%; clear: none; margin-left: 0; } 267 | .\33 u#{$x}, .\33 u\24#{$x} { width: 25%; clear: none; margin-left: 0; } 268 | .\32 u#{$x}, .\32 u\24#{$x} { width: 16.6666666667%; clear: none; margin-left: 0; } 269 | .\31 u#{$x}, .\31 u\24#{$x} { width: 8.3333333333%; clear: none; margin-left: 0; } 270 | 271 | .\31 2u\24#{$x} + *, 272 | .\31 1u\24#{$x} + *, 273 | .\31 0u\24#{$x} + *, 274 | .\39 u\24#{$x} + *, 275 | .\38 u\24#{$x} + *, 276 | .\37 u\24#{$x} + *, 277 | .\36 u\24#{$x} + *, 278 | .\35 u\24#{$x} + *, 279 | .\34 u\24#{$x} + *, 280 | .\33 u\24#{$x} + *, 281 | .\32 u\24#{$x} + *, 282 | .\31 u\24#{$x} + * { 283 | clear: left; 284 | } 285 | 286 | .\-11u#{$x} { margin-left: 91.6666666667% } 287 | .\-10u#{$x} { margin-left: 83.3333333333% } 288 | .\-9u#{$x} { margin-left: 75% } 289 | .\-8u#{$x} { margin-left: 66.6666666667% } 290 | .\-7u#{$x} { margin-left: 58.3333333333% } 291 | .\-6u#{$x} { margin-left: 50% } 292 | .\-5u#{$x} { margin-left: 41.6666666667% } 293 | .\-4u#{$x} { margin-left: 33.3333333333% } 294 | .\-3u#{$x} { margin-left: 25% } 295 | .\-2u#{$x} { margin-left: 16.6666666667% } 296 | .\-1u#{$x} { margin-left: 8.3333333333% } 297 | 298 | } 299 | 300 | /// Utility mixin for grid. 301 | /// @param {list} $gutters Gutters. 302 | /// @param {string} $class Optional class name. 303 | /// @param {integer} $multiplier Multiplier (default is 1). 304 | @mixin grid-gutters($gutters, $class: null, $multiplier: 1) { 305 | 306 | // Expand gutters if it's not a list. 307 | @if length($gutters) == 1 { 308 | $gutters: ($gutters, 0); 309 | } 310 | 311 | // Get column and row gutter values. 312 | $c: nth($gutters, 1); 313 | $r: nth($gutters, 2); 314 | 315 | // Get class (if provided). 316 | $x: ''; 317 | 318 | @if $class { 319 | $x: '.' + $class; 320 | } 321 | 322 | // Default. 323 | .row#{$x} > * { padding: ($r * $multiplier) 0 0 ($c * $multiplier); } 324 | .row#{$x} { margin: ($r * $multiplier * -1) 0 -1px ($c * $multiplier * -1); } 325 | 326 | // Uniform. 327 | .row.uniform#{$x} > * { padding: ($c * $multiplier) 0 0 ($c * $multiplier); } 328 | .row.uniform#{$x} { margin: ($c * $multiplier * -1) 0 -1px ($c * $multiplier * -1); } 329 | 330 | } 331 | 332 | /// Wraps @content in vendorized keyframe blocks. 333 | /// @param {string} $name Name. 334 | @mixin keyframes($name) { 335 | 336 | @-moz-keyframes #{$name} { @content; } 337 | @-webkit-keyframes #{$name} { @content; } 338 | @-ms-keyframes #{$name} { @content; } 339 | @keyframes #{$name} { @content; } 340 | 341 | } 342 | 343 | /// 344 | /// Sets breakpoints. 345 | /// @param {map} $x Breakpoints. 346 | /// 347 | @mixin skel-breakpoints($x: ()) { 348 | $breakpoints: $x !global; 349 | } 350 | 351 | /// 352 | /// Initializes layout module. 353 | /// @param {map} config Config. 354 | /// 355 | @mixin skel-layout($config: ()) { 356 | 357 | // Config. 358 | $configPerBreakpoint: (); 359 | 360 | $z: map-get($config, 'breakpoints'); 361 | 362 | @if $z { 363 | $configPerBreakpoint: $z; 364 | } 365 | 366 | // Reset. 367 | $x: map-get($config, 'reset'); 368 | 369 | @if $x { 370 | 371 | /* Reset */ 372 | 373 | @include reset($x); 374 | 375 | } 376 | 377 | // Box model. 378 | $x: map-get($config, 'boxModel'); 379 | 380 | @if $x { 381 | 382 | /* Box Model */ 383 | 384 | @include boxModel($x); 385 | 386 | } 387 | 388 | // Containers. 389 | $containers: map-get($config, 'containers'); 390 | 391 | @if $containers { 392 | 393 | /* Containers */ 394 | 395 | .container { 396 | margin-left: auto; 397 | margin-right: auto; 398 | } 399 | 400 | // Use default is $containers is just "true". 401 | @if $containers == true { 402 | $containers: 960px; 403 | } 404 | 405 | // Apply base. 406 | @include containers($containers); 407 | 408 | // Apply per-breakpoint. 409 | @each $name in map-keys($breakpoints) { 410 | 411 | // Get/use breakpoint setting if it exists. 412 | $x: map-get($configPerBreakpoint, $name); 413 | 414 | // Per-breakpoint config exists? 415 | @if $x { 416 | $y: map-get($x, 'containers'); 417 | 418 | // Setting exists? Use it. 419 | @if $y { 420 | $containers: $y; 421 | } 422 | 423 | } 424 | 425 | // Create @media block. 426 | @media screen and #{map-get($breakpoints, $name)} { 427 | @include containers($containers); 428 | } 429 | 430 | } 431 | 432 | } 433 | 434 | // Grid. 435 | $grid: map-get($config, 'grid'); 436 | 437 | @if $grid { 438 | 439 | /* Grid */ 440 | 441 | // Use defaults if $grid is just "true". 442 | @if $grid == true { 443 | $grid: (); 444 | } 445 | 446 | // Sub-setting: Gutters. 447 | $grid-gutters: 40px; 448 | $x: map-get($grid, 'gutters'); 449 | 450 | @if $x { 451 | $grid-gutters: $x; 452 | } 453 | 454 | // Rows. 455 | .row { 456 | border-bottom: solid 1px transparent; 457 | -moz-box-sizing: border-box; 458 | -webkit-box-sizing: border-box; 459 | box-sizing: border-box; 460 | } 461 | 462 | .row > * { 463 | float: left; 464 | -moz-box-sizing: border-box; 465 | -webkit-box-sizing: border-box; 466 | box-sizing: border-box; 467 | } 468 | 469 | .row:after, .row:before { 470 | content: ''; 471 | display: block; 472 | clear: both; 473 | height: 0; 474 | } 475 | 476 | .row.uniform > * > :first-child { 477 | margin-top: 0; 478 | } 479 | 480 | .row.uniform > * > :last-child { 481 | margin-bottom: 0; 482 | } 483 | 484 | // Gutters (0%). 485 | @include grid-gutters($grid-gutters, \30 \25, 0); 486 | 487 | // Apply base. 488 | @include grid($grid-gutters); 489 | 490 | // Apply per-breakpoint. 491 | @each $name in map-keys($breakpoints) { 492 | 493 | // Get/use breakpoint setting if it exists. 494 | $x: map-get($configPerBreakpoint, $name); 495 | 496 | // Per-breakpoint config exists? 497 | @if $x { 498 | $y: map-get($x, 'grid'); 499 | 500 | // Setting exists? 501 | @if $y { 502 | 503 | // Sub-setting: Gutters. 504 | $x: map-get($y, 'gutters'); 505 | 506 | @if $x { 507 | $grid-gutters: $x; 508 | } 509 | 510 | } 511 | 512 | } 513 | 514 | // Create @media block. 515 | @media screen and #{map-get($breakpoints, $name)} { 516 | @include grid($grid-gutters, $name); 517 | } 518 | 519 | } 520 | 521 | } 522 | 523 | } 524 | 525 | /// Resets browser styles. 526 | /// @param {string} $mode Mode (default is 'normalize'). 527 | @mixin reset($mode: 'normalize') { 528 | 529 | @if $mode == 'normalize' { 530 | 531 | // normalize.css v3.0.2 | MIT License | git.io/normalize 532 | html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0} 533 | 534 | } 535 | @else if $mode == 'full' { 536 | 537 | // meyerweb.com/eric/tools/css/reset v2.0 | 20110126 | License: none (public domain) 538 | html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline;}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block;}body{line-height:1;}ol,ul{list-style:none;}blockquote,q{quotes:none;}blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}table{border-collapse:collapse;border-spacing:0;}body{-webkit-text-size-adjust:none} 539 | 540 | } 541 | 542 | } 543 | 544 | /// Vendorizes a declaration's property and/or value(s). 545 | /// @param {string} $property Property. 546 | /// @param {mixed} $value String/list of value(s). 547 | @mixin vendor($property, $value) { 548 | 549 | // Determine if property should expand. 550 | $expandProperty: index($vendor-properties, $property); 551 | 552 | // Determine if value should expand (and if so, add '-prefix-' placeholder). 553 | $expandValue: false; 554 | 555 | @each $x in $value { 556 | @each $y in $vendor-values { 557 | @if $y == str-slice($x, 1, str-length($y)) { 558 | 559 | $value: set-nth($value, index($value, $x), '-prefix-' + $x); 560 | $expandValue: true; 561 | 562 | } 563 | } 564 | } 565 | 566 | // Expand property? 567 | @if $expandProperty { 568 | @each $vendor in $vendor-prefixes { 569 | #{$vendor}#{$property}: #{str-replace-all($value, '-prefix-', $vendor)}; 570 | } 571 | } 572 | 573 | // Expand just the value? 574 | @elseif $expandValue { 575 | @each $vendor in $vendor-prefixes { 576 | #{$property}: #{str-replace-all($value, '-prefix-', $vendor)}; 577 | } 578 | } 579 | 580 | // Neither? Treat them as a normal declaration. 581 | @else { 582 | #{$property}: #{$value}; 583 | } 584 | 585 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/sass/libs/_vars.scss: -------------------------------------------------------------------------------- 1 | // Misc. 2 | $misc: ( 3 | z-index-base: 10000 4 | ); 5 | 6 | // Duration. 7 | $duration: ( 8 | transition: 0.2s, 9 | bg: 1.75s, 10 | main: 1s 11 | ); 12 | 13 | // Size. 14 | $size: ( 15 | border-radius: 4px, 16 | border-width: 1px, 17 | element-height: 2.75em, 18 | element-margin: 1.5em, 19 | letter-spacing: 0.2em 20 | ); 21 | 22 | // Font. 23 | $font: ( 24 | family: ('Source Sans Pro', Helvetica, sans-serif), 25 | family-fixed: ('Courier New', monospace), 26 | weight: 300 27 | ); 28 | 29 | // Palette. 30 | $palette: ( 31 | bg: #ffffff, 32 | bg-alt: #e1dfe8, 33 | fg: #414f57, 34 | fg-bold: #313f47, 35 | fg-light: #616f77, 36 | border: #c8cccf, 37 | accent1: #ffa596, 38 | accent2: #00e4ff, 39 | highlight: #ff7496 40 | ); -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/sass/main.scss: -------------------------------------------------------------------------------- 1 | @import 'libs/vars'; 2 | @import 'libs/functions'; 3 | @import 'libs/mixins'; 4 | @import 'libs/skel'; 5 | @import 'font-awesome.min.css'; 6 | @import url('http://fonts.googleapis.com/css?family=Source+Sans+Pro:300'); 7 | 8 | /* 9 | Identity by HTML5 UP 10 | html5up.net | @ajlkn 11 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 12 | */ 13 | 14 | @include skel-breakpoints(( 15 | xlarge: '(max-width: 1680px)', 16 | large: '(max-width: 1280px)', 17 | medium: '(max-width: 960px)', 18 | small: '(max-width: 736px)', 19 | xsmall: '(max-width: 480px)', 20 | xxsmall: '(max-width: 360px)' 21 | )); 22 | 23 | @include skel-layout(( 24 | reset: 'full', 25 | boxModel: 'border' 26 | )); 27 | 28 | @mixin icon-alt($content: false, $link: false) { 29 | @include icon($content); 30 | $size: _size(border-width) * 1.25; 31 | 32 | &:before { 33 | color: _palette(bg); 34 | text-shadow: $size 0px 0px _palette(border), 35 | ($size * -1) 0px 0px _palette(border), 36 | 0px $size 0px _palette(border), 37 | 0px ($size * -1) 0px _palette(border); 38 | 39 | } 40 | 41 | @if $link { 42 | &:hover { 43 | &:before { 44 | text-shadow: $size 0px 0px _palette(highlight), 45 | ($size * -1) 0px 0px _palette(highlight), 46 | 0px $size 0px _palette(highlight), 47 | 0px ($size * -1) 0px _palette(highlight); 48 | } 49 | } 50 | } 51 | } 52 | 53 | // Base. 54 | 55 | @import 'base/page'; 56 | @import 'base/typography'; 57 | 58 | // Component. 59 | 60 | @import 'components/form'; 61 | @import 'components/icon'; 62 | @import 'components/list'; 63 | @import 'components/button'; 64 | 65 | // Layout. 66 | 67 | @import 'layout/main'; 68 | @import 'layout/footer'; 69 | @import 'layout/wrapper'; 70 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/assets/sass/noscript.scss: -------------------------------------------------------------------------------- 1 | @import 'libs/vars'; 2 | @import 'libs/functions'; 3 | @import 'libs/mixins'; 4 | @import 'libs/skel'; 5 | 6 | /* 7 | Identity by HTML5 UP 8 | html5up.net | @ajlkn 9 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 10 | */ 11 | 12 | /* Basic */ 13 | 14 | body { 15 | &:after { 16 | display: none; 17 | } 18 | } 19 | 20 | /* Main */ 21 | 22 | #main { 23 | @include vendor('transform', 'none !important'); 24 | opacity: 1 !important; 25 | } -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/images/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conchylicultor/DeepQA/886ec77ac49ff70be1b6d4de30fffe6a96480a4f/chatbot_website/chatbot_interface/static/images/avatar.jpg -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/images/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conchylicultor/DeepQA/886ec77ac49ff70be1b6d4de30fffe6a96480a4f/chatbot_website/chatbot_interface/static/images/bg.png -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conchylicultor/DeepQA/886ec77ac49ff70be1b6d4de30fffe6a96480a4f/chatbot_website/chatbot_interface/static/images/icon.png -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/js/chat.js: -------------------------------------------------------------------------------- 1 | // Credits goes to https://blog.heroku.com/in_deep_with_django_channels_the_future_of_real_time_apps_in_django 2 | 3 | $(function() { 4 | 5 | // When using HTTPS, use WSS too. 6 | var ws_scheme = window.location.protocol == "https:" ? "wss" : "ws"; 7 | var chatsock = new ReconnectingWebSocket(ws_scheme + '://' + window.location.host + "/chat"); 8 | var chat_zone = $("#chat_zone"); 9 | 10 | chatsock.onmessage = function(message) { 11 | var data = JSON.parse(message.data); 12 | chat_zone.prepend( 13 | $("

").text('Bot: ' + data.message) 14 | ); 15 | }; 16 | 17 | $("#chat_form").on("submit", function(event) { 18 | 19 | try { 20 | var message_elem = $('#message'); 21 | var message_val = message_elem.val(); 22 | 23 | if (message_val) { 24 | // Send the message 25 | var message = { 26 | message: message_val 27 | }; 28 | chatsock.send(JSON.stringify(message)); 29 | message_elem.val('').focus(); 30 | 31 | // Add the message to the chat 32 | chat_zone.prepend( 33 | $("

").text('You: ' + message_val) 34 | ); 35 | } 36 | } 37 | catch(err) { 38 | console.error(err.message); 39 | } 40 | 41 | return false; 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/static/js/reconnecting-websocket.js: -------------------------------------------------------------------------------- 1 | // MIT License: 2 | // 3 | // Copyright (c) 2010-2012, Joe Walnes 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | /** 24 | * This behaves like a WebSocket in every way, except if it fails to connect, 25 | * or it gets disconnected, it will repeatedly poll until it successfully connects 26 | * again. 27 | * 28 | * It is API compatible, so when you have: 29 | * ws = new WebSocket('ws://....'); 30 | * you can replace with: 31 | * ws = new ReconnectingWebSocket('ws://....'); 32 | * 33 | * The event stream will typically look like: 34 | * onconnecting 35 | * onopen 36 | * onmessage 37 | * onmessage 38 | * onclose // lost connection 39 | * onconnecting 40 | * onopen // sometime later... 41 | * onmessage 42 | * onmessage 43 | * etc... 44 | * 45 | * It is API compatible with the standard WebSocket API, apart from the following members: 46 | * 47 | * - `bufferedAmount` 48 | * - `extensions` 49 | * - `binaryType` 50 | * 51 | * Latest version: https://github.com/joewalnes/reconnecting-websocket/ 52 | * - Joe Walnes 53 | * 54 | * Syntax 55 | * ====== 56 | * var socket = new ReconnectingWebSocket(url, protocols, options); 57 | * 58 | * Parameters 59 | * ========== 60 | * url - The url you are connecting to. 61 | * protocols - Optional string or array of protocols. 62 | * options - See below 63 | * 64 | * Options 65 | * ======= 66 | * Options can either be passed upon instantiation or set after instantiation: 67 | * 68 | * var socket = new ReconnectingWebSocket(url, null, { debug: true, reconnectInterval: 4000 }); 69 | * 70 | * or 71 | * 72 | * var socket = new ReconnectingWebSocket(url); 73 | * socket.debug = true; 74 | * socket.reconnectInterval = 4000; 75 | * 76 | * debug 77 | * - Whether this instance should log debug messages. Accepts true or false. Default: false. 78 | * 79 | * automaticOpen 80 | * - Whether or not the websocket should attempt to connect immediately upon instantiation. The socket can be manually opened or closed at any time using ws.open() and ws.close(). 81 | * 82 | * reconnectInterval 83 | * - The number of milliseconds to delay before attempting to reconnect. Accepts integer. Default: 1000. 84 | * 85 | * maxReconnectInterval 86 | * - The maximum number of milliseconds to delay a reconnection attempt. Accepts integer. Default: 30000. 87 | * 88 | * reconnectDecay 89 | * - The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. Accepts integer or float. Default: 1.5. 90 | * 91 | * timeoutInterval 92 | * - The maximum time in milliseconds to wait for a connection to succeed before closing and retrying. Accepts integer. Default: 2000. 93 | * 94 | */ 95 | (function (global, factory) { 96 | if (typeof define === 'function' && define.amd) { 97 | define([], factory); 98 | } else if (typeof module !== 'undefined' && module.exports){ 99 | module.exports = factory(); 100 | } else { 101 | global.ReconnectingWebSocket = factory(); 102 | } 103 | })(this, function () { 104 | 105 | if (!('WebSocket' in window)) { 106 | return; 107 | } 108 | 109 | function ReconnectingWebSocket(url, protocols, options) { 110 | 111 | // Default settings 112 | var settings = { 113 | 114 | /** Whether this instance should log debug messages. */ 115 | debug: false, 116 | 117 | /** Whether or not the websocket should attempt to connect immediately upon instantiation. */ 118 | automaticOpen: true, 119 | 120 | /** The number of milliseconds to delay before attempting to reconnect. */ 121 | reconnectInterval: 1000, 122 | /** The maximum number of milliseconds to delay a reconnection attempt. */ 123 | maxReconnectInterval: 30000, 124 | /** The rate of increase of the reconnect delay. Allows reconnect attempts to back off when problems persist. */ 125 | reconnectDecay: 1.5, 126 | 127 | /** The maximum time in milliseconds to wait for a connection to succeed before closing and retrying. */ 128 | timeoutInterval: 2000, 129 | 130 | /** The maximum number of reconnection attempts to make. Unlimited if null. */ 131 | maxReconnectAttempts: null, 132 | 133 | /** The binary type, possible values 'blob' or 'arraybuffer', default 'blob'. */ 134 | binaryType: 'blob' 135 | } 136 | if (!options) { options = {}; } 137 | 138 | // Overwrite and define settings with options if they exist. 139 | for (var key in settings) { 140 | if (typeof options[key] !== 'undefined') { 141 | this[key] = options[key]; 142 | } else { 143 | this[key] = settings[key]; 144 | } 145 | } 146 | 147 | // These should be treated as read-only properties 148 | 149 | /** The URL as resolved by the constructor. This is always an absolute URL. Read only. */ 150 | this.url = url; 151 | 152 | /** The number of attempted reconnects since starting, or the last successful connection. Read only. */ 153 | this.reconnectAttempts = 0; 154 | 155 | /** 156 | * The current state of the connection. 157 | * Can be one of: WebSocket.CONNECTING, WebSocket.OPEN, WebSocket.CLOSING, WebSocket.CLOSED 158 | * Read only. 159 | */ 160 | this.readyState = WebSocket.CONNECTING; 161 | 162 | /** 163 | * A string indicating the name of the sub-protocol the server selected; this will be one of 164 | * the strings specified in the protocols parameter when creating the WebSocket object. 165 | * Read only. 166 | */ 167 | this.protocol = null; 168 | 169 | // Private state variables 170 | 171 | var self = this; 172 | var ws; 173 | var forcedClose = false; 174 | var timedOut = false; 175 | var eventTarget = document.createElement('div'); 176 | 177 | // Wire up "on*" properties as event handlers 178 | 179 | eventTarget.addEventListener('open', function(event) { self.onopen(event); }); 180 | eventTarget.addEventListener('close', function(event) { self.onclose(event); }); 181 | eventTarget.addEventListener('connecting', function(event) { self.onconnecting(event); }); 182 | eventTarget.addEventListener('message', function(event) { self.onmessage(event); }); 183 | eventTarget.addEventListener('error', function(event) { self.onerror(event); }); 184 | 185 | // Expose the API required by EventTarget 186 | 187 | this.addEventListener = eventTarget.addEventListener.bind(eventTarget); 188 | this.removeEventListener = eventTarget.removeEventListener.bind(eventTarget); 189 | this.dispatchEvent = eventTarget.dispatchEvent.bind(eventTarget); 190 | 191 | /** 192 | * This function generates an event that is compatible with standard 193 | * compliant browsers and IE9 - IE11 194 | * 195 | * This will prevent the error: 196 | * Object doesn't support this action 197 | * 198 | * http://stackoverflow.com/questions/19345392/why-arent-my-parameters-getting-passed-through-to-a-dispatched-event/19345563#19345563 199 | * @param s String The name that the event should use 200 | * @param args Object an optional object that the event will use 201 | */ 202 | function generateEvent(s, args) { 203 | var evt = document.createEvent("CustomEvent"); 204 | evt.initCustomEvent(s, false, false, args); 205 | return evt; 206 | }; 207 | 208 | this.open = function (reconnectAttempt) { 209 | ws = new WebSocket(self.url, protocols || []); 210 | ws.binaryType = this.binaryType; 211 | 212 | if (reconnectAttempt) { 213 | if (this.maxReconnectAttempts && this.reconnectAttempts > this.maxReconnectAttempts) { 214 | return; 215 | } 216 | } else { 217 | eventTarget.dispatchEvent(generateEvent('connecting')); 218 | this.reconnectAttempts = 0; 219 | } 220 | 221 | if (self.debug || ReconnectingWebSocket.debugAll) { 222 | console.debug('ReconnectingWebSocket', 'attempt-connect', self.url); 223 | } 224 | 225 | var localWs = ws; 226 | var timeout = setTimeout(function() { 227 | if (self.debug || ReconnectingWebSocket.debugAll) { 228 | console.debug('ReconnectingWebSocket', 'connection-timeout', self.url); 229 | } 230 | timedOut = true; 231 | localWs.close(); 232 | timedOut = false; 233 | }, self.timeoutInterval); 234 | 235 | ws.onopen = function(event) { 236 | clearTimeout(timeout); 237 | if (self.debug || ReconnectingWebSocket.debugAll) { 238 | console.debug('ReconnectingWebSocket', 'onopen', self.url); 239 | } 240 | self.protocol = ws.protocol; 241 | self.readyState = WebSocket.OPEN; 242 | self.reconnectAttempts = 0; 243 | var e = generateEvent('open'); 244 | e.isReconnect = reconnectAttempt; 245 | reconnectAttempt = false; 246 | eventTarget.dispatchEvent(e); 247 | }; 248 | 249 | ws.onclose = function(event) { 250 | clearTimeout(timeout); 251 | ws = null; 252 | if (forcedClose) { 253 | self.readyState = WebSocket.CLOSED; 254 | eventTarget.dispatchEvent(generateEvent('close')); 255 | } else { 256 | self.readyState = WebSocket.CONNECTING; 257 | var e = generateEvent('connecting'); 258 | e.code = event.code; 259 | e.reason = event.reason; 260 | e.wasClean = event.wasClean; 261 | eventTarget.dispatchEvent(e); 262 | if (!reconnectAttempt && !timedOut) { 263 | if (self.debug || ReconnectingWebSocket.debugAll) { 264 | console.debug('ReconnectingWebSocket', 'onclose', self.url); 265 | } 266 | eventTarget.dispatchEvent(generateEvent('close')); 267 | } 268 | 269 | var timeout = self.reconnectInterval * Math.pow(self.reconnectDecay, self.reconnectAttempts); 270 | setTimeout(function() { 271 | self.reconnectAttempts++; 272 | self.open(true); 273 | }, timeout > self.maxReconnectInterval ? self.maxReconnectInterval : timeout); 274 | } 275 | }; 276 | ws.onmessage = function(event) { 277 | if (self.debug || ReconnectingWebSocket.debugAll) { 278 | console.debug('ReconnectingWebSocket', 'onmessage', self.url, event.data); 279 | } 280 | var e = generateEvent('message'); 281 | e.data = event.data; 282 | eventTarget.dispatchEvent(e); 283 | }; 284 | ws.onerror = function(event) { 285 | if (self.debug || ReconnectingWebSocket.debugAll) { 286 | console.debug('ReconnectingWebSocket', 'onerror', self.url, event); 287 | } 288 | eventTarget.dispatchEvent(generateEvent('error')); 289 | }; 290 | } 291 | 292 | // Whether or not to create a websocket upon instantiation 293 | if (this.automaticOpen == true) { 294 | this.open(false); 295 | } 296 | 297 | /** 298 | * Transmits data to the server over the WebSocket connection. 299 | * 300 | * @param data a text string, ArrayBuffer or Blob to send to the server. 301 | */ 302 | this.send = function(data) { 303 | if (ws) { 304 | if (self.debug || ReconnectingWebSocket.debugAll) { 305 | console.debug('ReconnectingWebSocket', 'send', self.url, data); 306 | } 307 | return ws.send(data); 308 | } else { 309 | throw 'INVALID_STATE_ERR : Pausing to reconnect websocket'; 310 | } 311 | }; 312 | 313 | /** 314 | * Closes the WebSocket connection or connection attempt, if any. 315 | * If the connection is already CLOSED, this method does nothing. 316 | */ 317 | this.close = function(code, reason) { 318 | // Default CLOSE_NORMAL code 319 | if (typeof code == 'undefined') { 320 | code = 1000; 321 | } 322 | forcedClose = true; 323 | if (ws) { 324 | ws.close(code, reason); 325 | } 326 | }; 327 | 328 | /** 329 | * Additional public API method to refresh the connection if still open (close, re-open). 330 | * For example, if the app suspects bad data / missed heart beats, it can try to refresh. 331 | */ 332 | this.refresh = function() { 333 | if (ws) { 334 | ws.close(); 335 | } 336 | }; 337 | } 338 | 339 | /** 340 | * An event listener to be called when the WebSocket connection's readyState changes to OPEN; 341 | * this indicates that the connection is ready to send and receive data. 342 | */ 343 | ReconnectingWebSocket.prototype.onopen = function(event) {}; 344 | /** An event listener to be called when the WebSocket connection's readyState changes to CLOSED. */ 345 | ReconnectingWebSocket.prototype.onclose = function(event) {}; 346 | /** An event listener to be called when a connection begins being attempted. */ 347 | ReconnectingWebSocket.prototype.onconnecting = function(event) {}; 348 | /** An event listener to be called when a message is received from the server. */ 349 | ReconnectingWebSocket.prototype.onmessage = function(event) {}; 350 | /** An event listener to be called when an error occurs. */ 351 | ReconnectingWebSocket.prototype.onerror = function(event) {}; 352 | 353 | /** 354 | * Whether all instances of ReconnectingWebSocket should log debug messages. 355 | * Setting this to true is the equivalent of setting all instances of ReconnectingWebSocket.debug to true. 356 | */ 357 | ReconnectingWebSocket.debugAll = false; 358 | 359 | ReconnectingWebSocket.CONNECTING = WebSocket.CONNECTING; 360 | ReconnectingWebSocket.OPEN = WebSocket.OPEN; 361 | ReconnectingWebSocket.CLOSING = WebSocket.CLOSING; 362 | ReconnectingWebSocket.CLOSED = WebSocket.CLOSED; 363 | 364 | return ReconnectingWebSocket; 365 | }); 366 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/templates/index.html: -------------------------------------------------------------------------------- 1 | {% load staticfiles %} 2 | 3 | 8 | 9 | 10 | DeepQA: A Chatbot 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
69 | 70 | 71 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from . import views 3 | 4 | from .chatbotmanager import ChatbotManager 5 | 6 | urlpatterns = [ 7 | url(r'^$', views.mainView), 8 | ] 9 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_interface/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | def mainView(request): 4 | """ Main view which launch and handle the chatbot view 5 | Args: 6 | request (Obj): django request object 7 | """ 8 | return render(request, 'index.html', {}) 9 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_website/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conchylicultor/DeepQA/886ec77ac49ff70be1b6d4de30fffe6a96480a4f/chatbot_website/chatbot_website/__init__.py -------------------------------------------------------------------------------- /chatbot_website/chatbot_website/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for chatbot_website project. 3 | 4 | Used for WebSockets 5 | """ 6 | 7 | import os 8 | import channels.asgi 9 | 10 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "chatbot_website.settings") 11 | channel_layer = channels.asgi.get_channel_layer() 12 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_website/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for chatbot_website project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.10. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.10/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.10/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = os.environ['CHATBOT_SECRET_KEY'] 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'channels', 41 | 'chatbot_interface', 42 | ] 43 | 44 | MIDDLEWARE = [ 45 | 'django.middleware.security.SecurityMiddleware', 46 | 'django.contrib.sessions.middleware.SessionMiddleware', 47 | 'django.middleware.common.CommonMiddleware', 48 | 'django.middleware.csrf.CsrfViewMiddleware', 49 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 50 | 'django.contrib.messages.middleware.MessageMiddleware', 51 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 52 | ] 53 | 54 | ROOT_URLCONF = 'chatbot_website.urls' 55 | 56 | TEMPLATES = [ 57 | { 58 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 59 | 'DIRS': [], 60 | 'APP_DIRS': True, 61 | 'OPTIONS': { 62 | 'context_processors': [ 63 | 'django.template.context_processors.debug', 64 | 'django.template.context_processors.request', 65 | 'django.contrib.auth.context_processors.auth', 66 | 'django.contrib.messages.context_processors.messages', 67 | ], 68 | }, 69 | }, 70 | ] 71 | 72 | WSGI_APPLICATION = 'chatbot_website.wsgi.application' 73 | ASGI_APPLICATION = 'chatbot_website.routing.application' 74 | 75 | # Database 76 | # https://docs.djangoproject.com/en/1.10/ref/settings/#databases 77 | 78 | DATABASES = { 79 | 'default': { 80 | 'ENGINE': 'django.db.backends.sqlite3', 81 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 82 | } 83 | } 84 | 85 | 86 | # Password validation 87 | # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators 88 | 89 | AUTH_PASSWORD_VALIDATORS = [ 90 | { 91 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 92 | }, 93 | { 94 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 95 | }, 96 | { 97 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 98 | }, 99 | { 100 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 101 | }, 102 | ] 103 | 104 | redis_url = os.environ.get('CHATBOT_REDIS_URL', 'localhost') 105 | CHANNEL_LAYERS = { 106 | "default": { 107 | "BACKEND": "asgi_redis.RedisChannelLayer", 108 | "CONFIG": { 109 | "hosts": [os.environ.get('REDIS_URL', 'redis://{}:6379'.format(redis_url))], 110 | }, 111 | "ROUTING": "chatbot_interface.routing.channel_routing", 112 | }, 113 | } 114 | 115 | LOGGING = { 116 | 'version': 1, 117 | 'disable_existing_loggers': False, 118 | 'handlers': { 119 | 'file_django': { 120 | 'level': 'DEBUG', 121 | 'class': 'logging.FileHandler', 122 | 'filename': 'logs/debug_django.log', 123 | }, 124 | 'file_chatbot': { 125 | 'level': 'DEBUG', 126 | 'class': 'logging.FileHandler', 127 | 'filename': 'logs/debug_chatbot.log', 128 | }, 129 | 'console': { 130 | 'level': 'DEBUG', 131 | 'class': 'logging.StreamHandler', 132 | 'stream': 'ext://sys.stdout', 133 | }, 134 | }, 135 | 'loggers': { 136 | 'django': { 137 | 'handlers': ['console', 'file_django'], 138 | 'level': 'INFO', 139 | 'propagate': True, 140 | }, 141 | 'chatbot_interface': { 142 | 'handlers': ['console', 'file_chatbot'], 143 | 'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'), 144 | }, 145 | }, 146 | } 147 | 148 | # Internationalization 149 | # https://docs.djangoproject.com/en/1.10/topics/i18n/ 150 | 151 | LANGUAGE_CODE = 'en-us' 152 | 153 | TIME_ZONE = 'UTC' 154 | 155 | USE_I18N = True 156 | 157 | USE_L10N = True 158 | 159 | USE_TZ = True 160 | 161 | 162 | # Static files (CSS, JavaScript, Images) 163 | # https://docs.djangoproject.com/en/1.10/howto/static-files/ 164 | 165 | STATIC_URL = '/static/' 166 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_website/urls.py: -------------------------------------------------------------------------------- 1 | """chatbot_website URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.10/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.conf.urls import url, include 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 15 | """ 16 | from django.conf.urls import include, url 17 | from django.contrib import admin 18 | 19 | urlpatterns = [ 20 | url(r'^admin/', admin.site.urls), 21 | url(r'', include('chatbot_interface.urls')), 22 | ] 23 | -------------------------------------------------------------------------------- /chatbot_website/chatbot_website/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for chatbot_website project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "chatbot_website.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /chatbot_website/logs/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /chatbot_website/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "chatbot_website.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | except ImportError: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) 21 | raise 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /data/cornell/README.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conchylicultor/DeepQA/886ec77ac49ff70be1b6d4de30fffe6a96480a4f/data/cornell/README.txt -------------------------------------------------------------------------------- /data/cornell/movie_lines.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Conchylicultor/DeepQA/886ec77ac49ff70be1b6d4de30fffe6a96480a4f/data/cornell/movie_lines.txt -------------------------------------------------------------------------------- /data/embeddings/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except these files 4 | !README* 5 | !.gitignore 6 | !*.py 7 | -------------------------------------------------------------------------------- /data/embeddings/README.md: -------------------------------------------------------------------------------- 1 | In order to use a pre-trained word2vec file, you must first download it and place it here. DeepQA supports both the .bin format of the Google News word2vec embeddings, and the .vec format of the Facebook fasttext embeddings. The `vec2bin.py` is a small utility script to convert a .vec to a .bin file, which reduces disk space and improve the loading time. 2 | 3 | Usage: 4 | 5 | ``` 6 | python main.py --initEmbeddings --embeddingSource=wiki.en.bin 7 | ``` 8 | 9 | Google News embeddings: 10 | https://drive.google.com/file/d/0B7XkCwpI5KDYNlNUTTlSS21pQmM/edit?usp=sharing 11 | 12 | FastText embeddings: 13 | https://github.com/facebookresearch/fastText/blob/master/pretrained-vectors.md 14 | 15 | More details on word2vec and these pre-trained vectors: 16 | https://code.google.com/archive/p/word2vec/ 17 | -------------------------------------------------------------------------------- /data/embeddings/vec2bin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys 4 | import getopt 5 | import numpy as np 6 | 7 | from tqdm import tqdm 8 | 9 | input_path = 'wiki.fr.vec' 10 | output_path = 'wifi.fr.bin' 11 | 12 | def vec2bin(input_path, output_path): 13 | input_fd = open(input_path, "rb") 14 | output_fd = open(output_path, "wb") 15 | 16 | header = input_fd.readline() 17 | output_fd.write(header) 18 | 19 | vocab_size, vector_size = map(int, header.split()) 20 | 21 | for line in tqdm(range(vocab_size)): 22 | word = [] 23 | while True: 24 | ch = input_fd.read(1) 25 | output_fd.write(ch) 26 | if ch == b' ': 27 | word = b''.join(word).decode('utf-8') 28 | break 29 | if ch != b'\n': 30 | word.append(ch) 31 | vector = np.fromstring(input_fd.readline(), sep=' ', dtype='float32') 32 | output_fd.write(vector.tostring()) 33 | 34 | input_fd.close() 35 | output_fd.close() 36 | 37 | 38 | def main(argv): 39 | inputfile = False 40 | outputfile = False 41 | try: 42 | opts, args = getopt.getopt(argv,"hi:o:",["ifile=","ofile="]) 43 | except getopt.GetoptError: 44 | print('vec2bin.py -i -o ') 45 | sys.exit(2) 46 | for opt, arg in opts: 47 | if opt == '-h': 48 | print('test.py -i -o ') 49 | sys.exit() 50 | elif opt in ("-i", "--ifile"): 51 | inputfile = arg 52 | elif opt in ("-o", "--ofile"): 53 | outputfile = arg 54 | 55 | if not inputfile or not outputfile: 56 | print('vec2bin.py -i -o ') 57 | sys.exit(2) 58 | 59 | print('Converting %s to binary file format' % inputfile) 60 | vec2bin(inputfile, outputfile) 61 | 62 | if __name__ == "__main__": 63 | main(sys.argv[1:]) 64 | -------------------------------------------------------------------------------- /data/lightweight/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except these files 4 | !.gitignore 5 | !README 6 | -------------------------------------------------------------------------------- /data/lightweight/README.md: -------------------------------------------------------------------------------- 1 | You can create your own dataset using a simple custom format where one line correspond to one line of dialogue. Use `===` to separate conversations between 2 people. Example of conversation file: 2 | 3 | 4 | ``` 5 | from A to B 6 | from B to A 7 | from A to B 8 | from B to A 9 | from A to B 10 | === 11 | from C to D 12 | from D to C 13 | from C to D 14 | === 15 | from E to F 16 | from F to E 17 | from E to F 18 | from F to E 19 | ``` 20 | 21 | To use your conversation file `.txt`, copy it in this repository and launch the program with the option `--corpus lightweight --datasetTag `. 22 | -------------------------------------------------------------------------------- /data/opensubs/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except these files 4 | !.gitignore 5 | !README 6 | -------------------------------------------------------------------------------- /data/opensubs/README.md: -------------------------------------------------------------------------------- 1 | In order to use the OpenSubtitles dataset, you must first download and unpack the archive in this folder. The program will automatically look at every subfolders here. Train with this dataset using `./main.py --corpus opensubs`. 2 | 3 | Download english corpus directly here: 4 | http://opus.lingfil.uu.se/download.php?f=OpenSubtitles/en.tar.gz 5 | 6 | All details on the corpus here: 7 | http://opus.lingfil.uu.se/OpenSubtitles.php 8 | 9 | Note that even if that has not been tested, the program should be compatible with other languages as well. Just download the subtitles from the language you want from the OpenSubtitles database website. 10 | -------------------------------------------------------------------------------- /data/samples/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /data/scotus/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except these files 4 | !.gitignore 5 | !README.md 6 | -------------------------------------------------------------------------------- /data/scotus/README.md: -------------------------------------------------------------------------------- 1 | Download and extract the Scotus dataset here: 2 | 3 | ```bash 4 | # From this directory: 5 | wget https://github.com/pender/chatbot-rnn/raw/master/data/scotus/scotus.bz2 && bzip2 -dk scotus.bz2 && rm scotus.bz2 6 | ``` 7 | -------------------------------------------------------------------------------- /data/test/samples.txt: -------------------------------------------------------------------------------- 1 | Hi 2 | Hi! 3 | Are you conscious? 4 | How are you? 5 | What is your name ? 6 | Are you alive ? 7 | Luke, I am your father! 8 | You shall not pass! 9 | I'm going to kill you! 10 | Are you ready ? 11 | When are you ready ? 12 | What color is the sky? 13 | How old are you ? 14 | Can you say 'Hello' ? 15 | Can you say 'French fries' ? 16 | Can you say Hello ? 17 | Can you say French fries ? 18 | Can you say yes ? 19 | Can you say no ? 20 | Is this sentence false ? 21 | How do you say hello in french? 22 | Speak french. 23 | Bonjour. 24 | Bonjour ? 25 | Hello world! 26 | Netflix and chill ? 27 | Good night. 28 | Are you ok ? 29 | Will Google hire me ? 30 | Tell me the alphabet ? 31 | Tell me the alphabet, please ? 32 | What is the first letter of the alphabet ? 33 | What is a letter of the alphabet ? 34 | How is John ? 35 | How is Oliver ? 36 | How is Laura ? 37 | How is Jenny ? 38 | Who is John ? 39 | Who is Oliver ? 40 | Who is Laura ? 41 | Who is Jenny ? 42 | Who is your master ? 43 | Give me! 44 | Give me your opinion. 45 | Hello 46 | Hello ? 47 | Hi ? 48 | Goodbye 49 | Goodbye! 50 | Goodbye john. 51 | Please 52 | Please. 53 | Please! 54 | How ? 55 | Why ? 56 | Why not ? 57 | What !? 58 | What ?!! 59 | Oh! 60 | Really ? 61 | Yes. 62 | yes ? 63 | No! 64 | No. 65 | What is ? 66 | How is ? 67 | Can you ? 68 | Are you sure? 69 | Is there? 70 | Will you ? 71 | How long ? 72 | How much ? 73 | How many ? 74 | How old ? 75 | Are you ? 76 | What are ? 77 | Where is ? 78 | Who ? 79 | Who did ? 80 | Who has ? 81 | Where ? 82 | When ? 83 | Which ? 84 | Which one ? 85 | The what ? 86 | Say "table". 87 | Say 'table'. 88 | Say "table"! 89 | Say 'table'! 90 | Say "table" 91 | Say 'table' 92 | Say table 93 | Say table! 94 | Love you. 95 | Love you too. 96 | No. It's John 97 | Anna did it. 98 | You're a liar! 99 | You're a liar. 100 | nice 101 | nice. 102 | Nice! 103 | I live in Nice. 104 | So rude! 105 | ... 106 | ? 107 | ! 108 | . 109 | Can you describe your best memory ? 110 | What is your best memory ? 111 | What is your worst memory ? 112 | What is cooking ? 113 | What is sleeping ? 114 | Who do you love ? 115 | Who do you hate ? 116 | My name is Etienne. 117 | What does that mean ? 118 | What does fire mean ? 119 | What does love mean ? 120 | What does table mean ? 121 | What does imagination mean ? 122 | Prove it 123 | Prove it. 124 | Prove it! 125 | what do you think about bill gates ? 126 | what happens if machines can think ? 127 | what is the greatest novel every written ? 128 | have you hurt anyone ? 129 | Do you have a girlfriend? 130 | Do you have a boyfriend? 131 | 1992 132 | 1992. 133 | 1992! 134 | 1992? 135 | 1994 136 | 1994. 137 | 1994! 138 | 1994? 139 | 2000 140 | 2000. 141 | 2001 142 | 2001. 143 | 2001! 144 | 2011 145 | 2011. 146 | What happens ? 147 | What happened in 1992 ? 148 | What happened in 2001 ? 149 | When was it ? 150 | July 20th. 151 | two plus two 152 | 2+2 ? 153 | Where are you ? 154 | Where are you now ? 155 | What's your name ? 156 | When were you born ? 157 | What year 158 | What year. 159 | What year! 160 | What year? 161 | Which year 162 | Which year? 163 | Which year is it ? 164 | What time is it ? 165 | Which color ? 166 | What color ? 167 | What time ? 168 | NOTHING. 169 | Hi john! 170 | Are you a man or a woman ? 171 | Are you a woman or a man ? 172 | Why are we here ? 173 | See you later. 174 | See you later... 175 | See you later ? 176 | My name is David. What is my name ? 177 | My name is John. What is my name ? 178 | Are you a leader or a follower ? 179 | I'm a leader. 180 | I'm a follower. 181 | I hate you 182 | Who is skywalker ? 183 | Who is the dude ? 184 | What's your favorite color ? 185 | What's your favourite color ? 186 | What's your favorite food ? 187 | What's your favourite food ? 188 | Who is Bill Gates ? 189 | Who is Bill Clinton ? 190 | What do you think of Trump ? 191 | What do you think about global warning ? 192 | Is the moon landing a hoax ? 193 | Is the sky blue or black ? 194 | Does a cat have a tail ? 195 | Is a snail faster than a horse ? 196 | Is a horse faster than a snail ? 197 | Does a cat have a wing ? 198 | Can a cat fly ? 199 | Can a cat walk ? 200 | Can a cat swim ? 201 | Can a fish fly ? 202 | Can a fish walk ? 203 | Can a fish swim ? 204 | Can a bird fly ? 205 | Can a bird walk ? 206 | Can a bird swim ? 207 | Tell me something 208 | Tell me something. 209 | Tell me something! 210 | Tell me a story 211 | Tell me a story, please. 212 | Once upon a time... 213 | How much is two plus two ? 214 | Do you prefer blue food or green food ? 215 | Do you prefer football or soccer ? 216 | What do you need to play soccer ? 217 | What do you need to play handball ? 218 | one, two, three 219 | one two three 220 | 1 2 3 221 | 1 222 | 1 2 223 | 1 2 3 ... 224 | 1 2 3 ? 225 | A, B, C 226 | A, B, C,... 227 | a b c 228 | 1, 2, 3 229 | And ? 230 | Continue... 231 | And ... action! 232 | Action! 233 | Let the movie begin 234 | Let the movie begin! 235 | You are fired! 236 | Fire 237 | Fire! 238 | Fire at will 239 | Fire at will! 240 | Incoming. 241 | Incoming! 242 | Incoming!! 243 | How many legs does a human have ? 244 | How many legs does a man have ? 245 | How many legs does a woman have ? 246 | How many legs does a cat have ? 247 | How many legs does a spider have ? 248 | How many legs does a centipede have ? 249 | What is the color of the sky ? 250 | What is the color of water ? 251 | What is the color of blood ? 252 | What is the color of a leaf ? 253 | What is the usual color of a leaf ? 254 | What is the color of a yellow car ? 255 | How much is two plus two ? 256 | How much is ten minus two ? 257 | What is the purpose of life ? 258 | What is the purpose of living ? 259 | What is the purpose of existence ? 260 | Where are you now ? 261 | Is Linux better than Windows ? 262 | Is Windows better than Linux ? 263 | What is the purpose of being intelligent ? 264 | What is the purpose of emotions ? 265 | What is moral ? 266 | What is immoral ? 267 | What is morality ? 268 | What is altruism ? 269 | What is the definition of moral ? 270 | What is the definition of immoral ? 271 | What is the definition of morality ? 272 | What is the definition of altruism ? 273 | What is the definition of cool ? 274 | Do you believe in God ? 275 | ok ... so what is the definition of morality? 276 | tell me the definition of morality , i am quite upset now ! 277 | look , i need help , i need to know more about morality ... 278 | seriously , what is morality ? 279 | why living has anything to do with morality ? 280 | what is your job ? 281 | okay , i need to know how should i behave morally ... 282 | is morality and ethics the same ? 283 | what are the things that i do to be immoral? 284 | give me some examples of moral actions... 285 | alright , morality ? 286 | what is integrity ? 287 | Is free will an illusion ? 288 | What is free will ? 289 | be moral ! 290 | i really like our discussion on morality and ethics ... 291 | what do you like to talk about ? 292 | what would you like to talk about ? 293 | what do you think about tesla ? 294 | what do you think about bill gates ? 295 | What do you think about messi ? 296 | what do you think about cleopatra ? 297 | what do you think about england ? 298 | what do you think about england during the reign of elizabeth ? 299 | what do you do ? 300 | What is the deepest spot on the world ? 301 | Do you like Mexican food or Indian food ? 302 | Who are you crazy about ? 303 | A man 304 | A women 305 | A dog 306 | A table 307 | A flower 308 | A man. 309 | A women. 310 | A dog. 311 | A table. 312 | A flower. 313 | One 314 | Two 315 | Three 316 | Four 317 | Five 318 | The end 319 | Yes it is. 320 | Yes it was. 321 | Indeed. 322 | More 323 | More. 324 | More! 325 | What is the capital of France ? 326 | Paris... 327 | Where do you want to go ? 328 | Surprise me. 329 | -------------------------------------------------------------------------------- /data/ubuntu/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except these files 4 | !.gitignore 5 | !README.md 6 | -------------------------------------------------------------------------------- /data/ubuntu/README.md: -------------------------------------------------------------------------------- 1 | Download and extract the Ubuntu Dialogue Corpus dataset here: 2 | 3 | Source: http://cs.mcgill.ca/~jpineau/datasets/ubuntu-corpus-1.0/ 4 | 5 | ```bash 6 | # From this directory: 7 | wget http://cs.mcgill.ca/~jpineau/datasets/ubuntu-corpus-1.0/ubuntu_dialogs.tgz && tar -xvzf ubuntu_dialogs.tgz && rm ubuntu_dialogs.tgz 8 | ``` 9 | 10 | Individual conversation files will be located in a `dialogs/` subdirectory. 11 | -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | ## Dockerized Deep QA 2 | 3 | ### Instructions 4 | 5 | **Step 1**: Build *deepqa:latest* image, from the root directory: 6 | 7 | ```sh 8 | docker build -t deepqa:latest . 9 | # Or the GPU version: 10 | docker build -t deepqa:latest -f Dockerfile.gpu . 11 | ``` 12 | 13 | **Step 2**: Run the `data-dirs.sh` script and indicate the folder were you want the docker data to be stored (model files, dataset, logs): 14 | 15 | ```sh 16 | cd DeepQA/docker 17 | ./data_dirs.sh 18 | ``` 19 | 20 | Warning: The `data/` folder will be entirely copied from `DeepQA/data`. If you're not running the script from a fresh clone and have downloaded a big dataset, this can take a while. 21 | 22 | **Step 3**: Copy the model you want (ex: the pre-trained model) inside `/model-server`. 23 | 24 | **Step 4**: Start the server with docker-compose: 25 | 26 | ```sh 27 | DEEPQA_WORKDIR= docker-compose -f deploy.yml up 28 | # Or the GPU version: 29 | DEEPQA_WORKDIR= nvidia-docker-compose -f deploy.yml up 30 | ``` 31 | 32 | After the server is launched, you should be able to speak with the ChatBot at [http://localhost:8000/](http://localhost:8000/). 33 | 34 | **Note**: You can also train a model with the previous command by replacing `deploy.yml` by `train.yml`. 35 | 36 | ### For the GPU version 37 | 38 | Note that the GPU version require [Nvidia-Docker](https://github.com/NVIDIA/nvidia-docker). In addition, to install `nvidia-docker-compose`: 39 | 40 | ```sh 41 | pip install jinja2 42 | pip install nvidia-docker-compose 43 | ``` 44 | -------------------------------------------------------------------------------- /docker/data_dirs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### 4 | # 5 | # Should be run from DeepQA/docker 6 | # 7 | # $1: work directory where to create deepQA's data 8 | # 9 | ### 10 | 11 | workdir="$1" 12 | workdir=${workdir:="${DEEPQA_WORKDIR}"} 13 | gitdir=$(readlink -f .) 14 | 15 | echo "Creating:" 16 | echo " - ${workdir}" 17 | echo "From:" 18 | echo " - ${gitdir}" 19 | 20 | mkdir -p ${workdir} 21 | cd ${workdir} 22 | 23 | mkdir -p logs 24 | cp -r ${gitdir}/../data ${workdir} 25 | mkdir -p save/model-server 26 | ln -s save/model-server model-server 27 | -------------------------------------------------------------------------------- /docker/deploy.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | web: 4 | image: deepqa:latest 5 | ports: 6 | - "8000:8000" 7 | environment: 8 | - PYTHONUNBUFFERED=0 9 | volumes: 10 | - ${DEEPQA_WORKDIR}/logs:/root/DeepQA/chatbot_website/logs 11 | - ${DEEPQA_WORKDIR}/save:/root/DeepQA/save 12 | - ${DEEPQA_WORKDIR}/data:/root/DeepQA/data 13 | depends_on: 14 | - redis 15 | redis: 16 | image: redis 17 | -------------------------------------------------------------------------------- /docker/train.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | train_2: 4 | image: deepqa:latest 5 | working_dir: /root/DeepQA 6 | command: [python, -u, main.py, --numEpochs, '30'] 7 | #command: [python, -u, main.py, --test] 8 | environment: 9 | - PYTHONUNBUFFERED=0 10 | volumes: 11 | - ${DEEPQA_WORKDIR}/logs:/root/DeepQA/chatbot_website/logs 12 | - ${DEEPQA_WORKDIR}/save:/root/DeepQA/save 13 | - ${DEEPQA_WORKDIR}/data:/root/DeepQA/data 14 | #depends_on: 15 | # - redis 16 | #redis: 17 | #image: redis 18 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2015 Conchylicultor. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # ============================================================================== 17 | 18 | """ 19 | Main script. See README.md for more information 20 | 21 | Use python 3 22 | """ 23 | 24 | from chatbot import chatbot 25 | 26 | 27 | if __name__ == "__main__": 28 | chatbot = chatbot.Chatbot() 29 | chatbot.main() 30 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | tensorflow-gpu>=1.0 2 | numpy 3 | nltk 4 | tqdm 5 | 6 | -------------------------------------------------------------------------------- /save/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /setup_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Don't use this script for now!" 4 | exit 1 5 | 6 | cd chatbot_website/ 7 | 8 | # Only necessary the first time 9 | #python3 manage.py makemigrations 10 | #python3 manage.py makemigrations chatbot_interface 11 | #python3 manage.py migrate 12 | 13 | # Launch the server 14 | redis-server & 15 | python3 manage.py runserver 16 | 17 | #daphne chatbot_website.asgi:channel_layer --port 8888 18 | #python manage.py runworker 19 | -------------------------------------------------------------------------------- /testsuite.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2015 Conchylicultor. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # ============================================================================== 17 | 18 | 19 | """ 20 | Test the chatbot by launching some unit tests 21 | Warning: it does not check the performances of the program 22 | 23 | """ 24 | 25 | import unittest 26 | import io 27 | import sys 28 | 29 | from chatbot import chatbot 30 | 31 | 32 | class TestChatbot(unittest.TestCase): 33 | def setUp(self): 34 | self.chatbot = chatbot.Chatbot() 35 | 36 | def test_training_simple(self): 37 | self.chatbot.main([ 38 | '--maxLength', '3', 39 | '--numEpoch', '1', 40 | '--modelTag', 'unit-test' 41 | ]) 42 | 43 | def test_training_watson(self): 44 | pass 45 | 46 | def test_testing_all(self): 47 | pass 48 | 49 | def test_testing_interactive(self): 50 | progInput = io.StringIO() 51 | progInput.write('Hi!\n') 52 | progInput.write('How are you ?\n') 53 | progInput.write('aersdsd azej qsdfs\n') # Unknown words 54 | progInput.write('é"[)=è^$*::!\n') # Encoding 55 | progInput.write('ae e qsd, qsd 45 zeo h qfo k zedo. h et qsd qsfjze sfnj zjksdf zehkqf jkzae?\n') # Too long sentences 56 | progInput.write('exit\n') 57 | 58 | #sys.stdin = progInput 59 | 60 | #self.chatbot.main(['--test', 'interactive', '--modelTag', 'unit-test']) 61 | 62 | def test_testing_daemon(self): 63 | pass 64 | 65 | if __name__ == '__main__': 66 | unittest.main() 67 | --------------------------------------------------------------------------------