├── .coveragerc ├── .editorconfig ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── AUTHORS.rst ├── CONTRIBUTING.rst ├── HISTORY.rst ├── LICENSE ├── MANIFEST ├── MANIFEST.in ├── Makefile ├── README.rst ├── django_ai.jpg ├── django_ai ├── __init__.py ├── apps.py ├── base │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── models.py │ ├── templates │ │ └── base │ │ │ ├── admin │ │ │ └── mixed_inlines_change_form.html │ │ │ └── snippets │ │ │ └── ai_actions.html │ ├── templatetags │ │ ├── __init__.py │ │ └── admin_extras.py │ ├── urls.py │ ├── utils.py │ └── views.py ├── bayesian_networks │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── bayespy_constants.py │ ├── models.py │ ├── static │ │ ├── css │ │ │ └── admin │ │ │ │ └── bayesian_networks.css │ │ └── js │ │ │ └── admin │ │ │ └── bayesian_networks.js │ ├── templates │ │ └── admin │ │ │ └── bayesian_networks │ │ │ └── bayesiannetwork │ │ │ ├── change_form.html │ │ │ └── snippets │ │ │ └── cm_table.html │ ├── tests.py │ ├── urls.py │ ├── utils.py │ └── views.py ├── django_ai │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── examples │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── metrics.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_student_avg1.py │ │ ├── 0003_populate_userinfo.py │ │ ├── 0004_bn_example.py │ │ ├── 0005_add_avg_times_clusters.py │ │ ├── 0006_clustering_bn_example.py │ │ ├── 0007_userinfo_cluster_1.py │ │ ├── 0008_add_visits_to_pages.py │ │ ├── 0009_rename_avg_time_logged.py │ │ ├── 0010_tweaks_on_userinfo.py │ │ ├── 0011_add_rest_avg_times_visits.py │ │ ├── 0012_update_clustering_bn.py │ │ ├── 0013_commentofmysite.py │ │ ├── 0014_missing_ui_meta.py │ │ ├── 0015_sfptenron_sfptyoutube.py │ │ ├── 0016_pretraining_minor_meta_update.py │ │ ├── 0017_nullboolean_for_coms_is_spam.py │ │ ├── 0018_spam_filter_example.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── examples │ │ │ ├── base.html │ │ │ ├── comments_of_my_site.html │ │ │ └── sample_page.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── manage.py ├── static │ ├── css │ │ └── django_ai.css │ ├── img │ │ └── .gitignore │ └── js │ │ └── django_ai.js ├── supervised_learning │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── models │ │ ├── __init__.py │ │ └── svm.py │ ├── tests.py │ └── views.py ├── systems │ ├── __init__.py │ └── spam_filtering │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── models.py │ │ ├── static │ │ └── js │ │ │ └── admin │ │ │ └── spam_filter.js │ │ ├── templates │ │ └── admin │ │ │ └── spam_filtering │ │ │ └── spamfilter │ │ │ ├── change_form.html │ │ │ └── snippets │ │ │ └── model_info.html │ │ ├── tests.py │ │ └── views.py └── templates │ └── django_ai │ └── base.html ├── docs ├── Makefile ├── _static │ └── django_ai.jpg ├── api │ └── api.rst ├── apps │ ├── base.rst │ ├── bayesian_networks.rst │ ├── examples.rst │ ├── index.rst │ ├── spam_filtering.rst │ └── supervised_learning.rst ├── authors.rst ├── conf.py ├── contributing.rst ├── history.rst ├── index.rst ├── installation.rst ├── intro.rst ├── make.bat └── readme.rst ├── manage.py ├── misc └── django_dag_0001_initial.py ├── requirements.txt ├── requirements_dev.txt ├── requirements_rtd.txt ├── requirements_test.txt ├── runtests.py ├── setup.cfg ├── setup.py ├── tests ├── __init__.py ├── apps │ ├── __init__.py │ ├── base │ │ ├── __init__.py │ │ └── test_base.py │ ├── bayesian_networks │ │ ├── __init__.py │ │ └── test_bns.py │ ├── supervised_learning │ │ ├── __init__.py │ │ └── test_svm.py │ └── systems │ │ ├── __init__.py │ │ └── test_spam_filtering.py ├── settings.py ├── test_models │ ├── __init__.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_populate_userinfo.py │ │ ├── 0003_add_avg_times_clusters.py │ │ ├── 0004_userinfo_cluster_1.py │ │ ├── 0005_rename_avg_time_logged.py │ │ ├── 0006_create_mystatmodel_and_alter_ui_avg_time_pages.py │ │ ├── 0007_userinfo2.py │ │ ├── 0008_mystatisticalmodel_has_results.py │ │ ├── 0009_mysupervisedlearningtechnique.py │ │ ├── 0010_myunsupervisedlearningtechnique.py │ │ ├── 0011_add_is_inferred_and_minor_tweaks.py │ │ ├── 0012_mysfpt_spammablemodel.py │ │ ├── 0013_nullboolean_for_sp_is_spam.py │ │ ├── 0014_add_new_fields_from_am.py │ │ └── __init__.py │ └── models.py └── urls.py └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = true 3 | 4 | [report] 5 | omit = 6 | *site-packages* 7 | *tests* 8 | *.tox* 9 | *settings* 10 | *apps.py 11 | *scratchpad.py 12 | *wsgi.py 13 | *__init__.py 14 | */migrations/* 15 | */examples/* 16 | *manage.py 17 | django_ai/django_ai/urls.py 18 | 19 | show_missing = True 20 | exclude_lines = 21 | pragma: no cover 22 | def __str__ 23 | raise NotImplementedError 24 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.{py,rst,ini}] 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [*.{html,css,scss,json,yml}] 16 | indent_style = space 17 | indent_size = 2 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | 22 | [Makefile] 23 | indent_style = tab 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | * django-ai version: 2 | * Django version: 3 | * Python version: 4 | * Operating System: 5 | 6 | ### Description 7 | 8 | Describe what you were trying to get done. 9 | Tell us what happened, what went wrong, and what you expected to happen. 10 | 11 | ### What I Did 12 | 13 | ``` 14 | Paste the command(s) you ran and the output. 15 | If there was a crash, please include the traceback here. 16 | ``` 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Installer logs 10 | pip-log.txt 11 | 12 | # Unit test / coverage reports 13 | .coverage 14 | .tox 15 | nosetests.xml 16 | htmlcov 17 | 18 | # Translations 19 | *.mo 20 | 21 | # Mr Developer 22 | .mr.developer.cfg 23 | .project 24 | .pydevproject 25 | 26 | # Pycharm/Intellij 27 | .idea 28 | 29 | # Complexity 30 | output/*.html 31 | output/*/index.html 32 | 33 | # Sphinx 34 | docs/_build 35 | 36 | # SQLite 37 | *.sqlite* 38 | 39 | # Ignore migrations in Techniques / Stat Models 40 | django_ai/bayesian_networks/migrations/* 41 | django_ai/base/migrations/* 42 | django_ai/supervised_learning/migrations/* 43 | django_ai/systems/spam_filtering/migrations/* 44 | 45 | # Ignore scratchpad 46 | scratchpad.* 47 | 48 | # Ignore .tar.gz and .zip files 49 | *.tar.gz 50 | *.zip 51 | 52 | # Ignore generated images 53 | django_ai/media/django_ai/bayesian_networks/* 54 | tests/media/* 55 | 56 | # Distribution / packaging 57 | .Python 58 | env/ 59 | build/ 60 | develop-eggs/ 61 | dist/ 62 | downloads/ 63 | eggs/ 64 | .eggs/ 65 | lib/ 66 | lib64/ 67 | parts/ 68 | sdist/ 69 | var/ 70 | wheels/ 71 | *.egg-info/ 72 | .installed.cfg 73 | *.egg 74 | 75 | # PyInstaller 76 | # Usually these files are written by a python script from a template 77 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 78 | *.manifest 79 | *.spec 80 | 81 | # Installer logs 82 | pip-log.txt 83 | pip-delete-this-directory.txt 84 | 85 | # Unit test / coverage reports 86 | htmlcov/ 87 | .tox/ 88 | .coverage 89 | .coverage.* 90 | .cache 91 | nosetests.xml 92 | coverage.xml 93 | *.cover 94 | .hypothesis/ 95 | 96 | # Translations 97 | *.mo 98 | *.pot 99 | 100 | # Django stuff: 101 | *.log 102 | local_settings.py 103 | 104 | # Flask stuff: 105 | instance/ 106 | .webassets-cache 107 | 108 | # Scrapy stuff: 109 | .scrapy 110 | 111 | # Sphinx documentation 112 | docs/_build/ 113 | 114 | # PyBuilder 115 | target/ 116 | 117 | # Jupyter Notebook 118 | .ipynb_checkpoints 119 | 120 | # pyenv 121 | .python-version 122 | 123 | # celery beat schedule file 124 | celerybeat-schedule 125 | 126 | # SageMath parsed files 127 | *.sage.py 128 | 129 | # dotenv 130 | .env 131 | 132 | # virtualenv 133 | .venv 134 | venv/ 135 | ENV/ 136 | 137 | # Spyder project settings 138 | .spyderproject 139 | .spyproject 140 | 141 | # Rope project settings 142 | .ropeproject 143 | 144 | # mkdocs documentation 145 | /site 146 | 147 | # mypy 148 | .mypy_cache/ 149 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Config file for automatic testing at travis-ci.org 2 | 3 | language: python 4 | 5 | python: 6 | - "3.5" 7 | 8 | env: 9 | - TOX_ENV=py35-django-111 PYTHONHASHSEED=0 10 | - TOX_ENV=py35-django-20 PYTHONHASHSEED=0 11 | 12 | matrix: 13 | fast_finish: true 14 | 15 | before_install: sudo apt-get install graphviz 16 | 17 | # command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors 18 | install: pip install -r requirements_test.txt 19 | 20 | # command to run tests using coverage, e.g. python setup.py test 21 | script: tox -e $TOX_ENV 22 | 23 | after_success: 24 | - codecov -e TOX_ENV 25 | 26 | sudo: required 27 | dist: trusty 28 | group: deprecated-2017Q4 29 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | Credits 3 | ======= 4 | 5 | Development and Proyect Lead 6 | ---------------------------- 7 | 8 | * Rodrigo Gadea 9 | 10 | Artwork Contributors 11 | -------------------- 12 | 13 | None yet. Why not be the first? 14 | 15 | Code Contributors 16 | ----------------- 17 | 18 | None yet. Why not be the first? 19 | 20 | Documentation Contributors 21 | -------------------------- 22 | 23 | None yet. Why not be the first? 24 | 25 | Monetary Contributors 26 | --------------------- 27 | 28 | None yet. Why not be the first? 29 | 30 | Non-Monetary Contributors 31 | ------------------------- 32 | 33 | None yet. Why not be the first? 34 | 35 | Promotion Contributors 36 | ---------------------- 37 | 38 | None yet. Why not be the first? 39 | 40 | Others 41 | ------ 42 | 43 | Tools used in rendering this package 44 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 45 | 46 | * Cookiecutter_ 47 | * `cookiecutter-djangopackage`_ 48 | 49 | .. _Cookiecutter: https://github.com/audreyr/cookiecutter 50 | .. _`cookiecutter-djangopackage`: https://github.com/pydanny/cookiecutter-djangopackage 51 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | .. _contributing: 2 | 3 | ============ 4 | Contributing 5 | ============ 6 | 7 | Contributions are welcome and highly appreciated!! Every little bit helps, and credit will always be given. 8 | 9 | You can contribute in many ways: 10 | 11 | Types of Contributions 12 | ---------------------- 13 | 14 | Report Bugs 15 | ~~~~~~~~~~~ 16 | 17 | Report bugs at https://github.com/math-a3k/django-ai/issues. 18 | 19 | If you are reporting a bug, please include: 20 | 21 | * Your operating system name and version. 22 | * Any details about your local setup that might be helpful in troubleshooting. 23 | * Detailed steps to reproduce the bug. 24 | 25 | Fix Bugs 26 | ~~~~~~~~ 27 | 28 | Look through the GitHub issues for bugs. Anything tagged with "bug" is open to whoever wants to implement it. 29 | 30 | Implement Features 31 | ~~~~~~~~~~~~~~~~~~ 32 | 33 | Look through the GitHub issues for features. Anything tagged with "feature" is open to whoever wants to implement it. 34 | 35 | Write Documentation 36 | ~~~~~~~~~~~~~~~~~~~ 37 | 38 | django-ai could always use more documentation, whether as part of the official django-ai docs, in docstrings, or even on the web in blog posts, articles, and such. Even questions in community sites as stackoverflow generates documentation :) 39 | 40 | Submit Feedback 41 | ~~~~~~~~~~~~~~~ 42 | 43 | The best way to send feedback is to file an issue at 44 | https://github.com/math-a3k/django-ai/issues. 45 | 46 | If you are proposing a feature: 47 | 48 | * Explain in detail how it would work. 49 | * Keep the scope as narrow as possible, to make it easier to implement. 50 | * Remember that this is a community project, and that contributions are welcome :) 51 | 52 | Although conversely you may use any of the communications channels - such as the mailing list - to send it, the important thing is the feedback, not the mean :) 53 | 54 | 55 | Artwork 56 | ~~~~~~~ 57 | 58 | Artwork - logos, banners, themes, etc. - is highly appreciated and always welcomed. 59 | 60 | 61 | Monetary 62 | ~~~~~~~~ 63 | 64 | You can support and ensure the `django-ai` development by making money arrive to the project in its different ways: 65 | 66 | Donations 67 | Software development has costs, any help for lessen them is highly appreciated and encourages a lot to keep going :) 68 | 69 | Sponsoring 70 | Hire the devs for working in a specific feature you would like to have or a bug to squash in a timely manner. 71 | 72 | Hiring, Contracting and Consultancy 73 | Hire the developers to work for you (implementing code, models, etc.) in its different modalities. Even if it is not `django-ai` related, as long as the devs have enough for a living, the project will keep evolving. 74 | 75 | 76 | Non-Monetary 77 | ~~~~~~~~~~~~ 78 | 79 | You can support and ensure the `django-ai` development by making any good or service arrive to the project. 80 | 81 | Anything that you consider that is helpful in the Software Development Process - as a whole - and the Proyect Sustainability is highly appreciated and encourages a lot to keep going :) 82 | 83 | 84 | Promotion 85 | ~~~~~~~~~ 86 | 87 | Blog posts, articles, talks, etc. Anything that improves the difussion of the project is also a Contribution and helps spreading it (in a wide sense). 88 | 89 | Get Started! 90 | ------------ 91 | 92 | Ready to contribute code or documentation? 93 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 94 | 95 | Here's how to set up `django-ai` for local development. 96 | 97 | 1. Fork the `django-ai` repo on GitHub. 98 | 2. Clone your fork locally:: 99 | 100 | $ git clone git@github.com:/django-ai.git 101 | 102 | 3. Install your local copy into a virtualenv. This is how you set up your fork for local development:: 103 | 104 | $ python3 -m venv django-ai-env 105 | $ source django-ai-env/bin/activate 106 | $ cd django-ai/ 107 | $ pip install -r requirements_dev.txt 108 | 109 | 4. Create a branch for local development:: 110 | 111 | $ git checkout -b name-of-your-bugfix-or-feature 112 | 113 | Now you can make your changes locally. 114 | 115 | 5. When you're done making changes, check that your changes pass flake8 reasonably (or your preferred pep8 linter) and the tests (including tox):: 116 | 117 | $ flake8 django_ai tests 118 | $ PYTHONHASHSEED=0 python runtests.py 119 | $ tox 120 | 121 | 6. Commit your changes and push your branch to GitHub:: 122 | 123 | $ git add . 124 | $ git commit -m "Your detailed description of your changes." 125 | $ git push origin name-of-your-bugfix-or-feature 126 | 127 | 7. Submit a pull request through the GitHub website to the ``development`` branch. Once your changes are reviewed, you may be assigned to review another pull request with improvements on your code if deemed necessary. Once we agree on a final result, it will be merged to ``master``. 128 | 129 | Pull Request Guidelines 130 | ~~~~~~~~~~~~~~~~~~~~~~~ 131 | 132 | Before you submit a pull request, check that it meets these guidelines: 133 | 134 | 1. The pull request should include tests. 135 | 2. If the pull request adds functionality, the docs should be updated. 136 | 3. The pull request should work for the building matrix of CI. Check https://travis-ci.org/math-a3k/django-ai/pull_requests and make sure that the tests pass for all supported environments. 137 | 138 | Tips 139 | ~~~~ 140 | 141 | To run a particular of test:: 142 | 143 | $ PYTHONHASHSEED=0 python runtests.py tests.test_bns.TestDjango_ai. 144 | 145 | Ready to make a monetary contribution? 146 | -------------------------------------- 147 | 148 | Contact the lead developer or use any of the communication channels and - no matter how micro it is - we will find a way of making it happen :) 149 | 150 | Ready to make a non-monetary contribution? 151 | ------------------------------------------ 152 | 153 | Contact the lead developer or use any of the communication channels and - no matter how micro it is - we will find a way of making it happen :) 154 | 155 | Ready to make a promotion contribution? 156 | --------------------------------------- 157 | 158 | Contact the lead developer or use any of the communication channels and it will be listed :) 159 | 160 | Ready to make an artwork contribution? 161 | -------------------------------------- 162 | 163 | If you don't feel comfortable with `git`, use the GitHub wiki - https://github.com/math-a3k/django-ai/wiki - and the mailing list for submitting - django-ai@googlegroups.com. -------------------------------------------------------------------------------- /HISTORY.rst: -------------------------------------------------------------------------------- 1 | .. :changelog: 2 | 3 | ======= 4 | History 5 | ======= 6 | 7 | 0.0.2 (2018-01-15) 8 | ++++++++++++++++++ 9 | 10 | django-ai 0.0.2 Release Notes 11 | ----------------------------- 12 | 13 | I'm very happy of announcing the second release of ``django-ai``: Artificial Intelligence for Django. 14 | 15 | The main exciting features of this version are Spam Filtering systems and Classification with Support Vector Machines ready to be plugged into any Django application. 16 | 17 | Spam Filtering 18 | ^^^^^^^^^^^^^^ 19 | 20 | This *system* uses the `scikit-learn framework `_ as engine and allows you to incorporate to any Django Model susceptible to spamming a self-updating filter capable of learning from labelled history to discern Spam content so you can act accordingly. 21 | 22 | The classifier can be chosen and all the parameters in the process can be fine-tunned conveniently via the admin front-end. See the :ref:`example ` and the :ref:`documentation ` for more. 23 | 24 | Plugging Spam Filters to your project has never been so easy!! :) 25 | 26 | Classification 27 | ^^^^^^^^^^^^^^ 28 | 29 | A new app is introduced: *Supervised Learning*, which provides Classification and Regression models ready to be plugged into ``django-ai`` systems or to be used stand-alone in your application where deemed necessary. 30 | 31 | Support Vector Machines (SVM), one of the most understood and best performing classifier for high-dimensional data is featured in this app. 32 | 33 | Others 34 | ^^^^^^ 35 | 36 | - Support for Django 2.0 37 | 38 | 0.0.1 (2017-11-13) 39 | ++++++++++++++++++ 40 | 41 | django-ai 0.0.1 Release Notes 42 | ----------------------------- 43 | 44 | I'm very happy to announce the first release of django-ai: Artificial Intelligence for Django!! 45 | 46 | ``django-ai`` is a collection of apps for integrating statistical models into your Django project so you can implement machine learning conveniently. 47 | 48 | It aims to integrate several libraries and engines providing your Django app with a set of tools so you can leverage your project functionality with the data generated within. 49 | 50 | The integration is done through Django models - where most of the data is generated and stored in a Django project - and an API focused on integrating seamlessly within Django projects’ best practices and patterns. 51 | 52 | The rationale of ``django-ai`` is to provide for each statistical model or technique bundled a front-end for configuration and an API for integrating it into your code. 53 | 54 | Excited? 55 | 56 | - :ref:`introduction` 57 | - :ref:`quickstart` 58 | - :ref:`examples` 59 | 60 | You are welcome to join the community of users and developers :) 61 | 62 | Features 63 | ^^^^^^^^ 64 | 65 | * Bayesian Networks: Integrate Bayesian Networks through your models using the `BayesPy framework `_. 66 | 67 | Known Issues 68 | ^^^^^^^^^^^^ 69 | 70 | * In development mode (``DEBUG = True``) the BayesPy Inference Engine may stall during model estimation on certain states of the Pseudo Random Number Generator. You may need to reset the PRNG or deactivate and activate again your Python virtualenv. This does not affect other operations like cluster assigment. 71 | 72 | 0.0.1a0 (2017-08-31) 73 | ++++++++++++++++++++ 74 | 75 | * First release on PyPI. 76 | 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | >>>>>>> d54b73db0354d77bb78e8227019fc985d41fa772 167 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | # file GENERATED by distutils, do NOT edit 2 | AUTHORS.rst 3 | CONTRIBUTING.rst 4 | HISTORY.rst 5 | LICENSE 6 | README.rst 7 | setup.cfg 8 | setup.py 9 | django_ai/__init__.py 10 | django_ai/apps.py 11 | django_ai/manage.py 12 | django_ai/bayesian_networks/__init__.py 13 | django_ai/bayesian_networks/admin.py 14 | django_ai/bayesian_networks/apps.py 15 | django_ai/bayesian_networks/bayespy_constants.py 16 | django_ai/bayesian_networks/models.py 17 | django_ai/bayesian_networks/tests.py 18 | django_ai/bayesian_networks/urls.py 19 | django_ai/bayesian_networks/views.py 20 | django_ai/bayesian_networks/migrations/0001_initial.py 21 | django_ai/bayesian_networks/migrations/0002_auto_20170901_2203.py 22 | django_ai/bayesian_networks/migrations/__init__.py 23 | django_ai/bayesian_networks/templates/admin/bayesian_networks/bayesiannetwork/change_form.html 24 | django_ai/django_ai/__init__.py 25 | django_ai/django_ai/settings.py 26 | django_ai/django_ai/urls.py 27 | django_ai/django_ai/wsgi.py 28 | django_ai/examples/__init__.py 29 | django_ai/examples/admin.py 30 | django_ai/examples/apps.py 31 | django_ai/examples/models.py 32 | django_ai/examples/tests.py 33 | django_ai/examples/views.py 34 | django_ai/examples/migrations/0001_initial.py 35 | django_ai/examples/migrations/0002_student_avg1.py 36 | django_ai/examples/migrations/0003_populate_userinfo.py 37 | django_ai/examples/migrations/__init__.py 38 | django_ai/media/django_ai/bayesian_networks/b1.png 39 | django_ai/static/css/django_ai.css 40 | django_ai/static/js/django_ai.js 41 | django_ai/templates/django_ai/base.html 42 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS.rst 2 | include CONTRIBUTING.rst 3 | include HISTORY.rst 4 | include LICENSE 5 | include README.rst 6 | recursive-include django_ai *.html *.png *.gif *js *.css *jpg *jpeg *svg *py 7 | exclude django_ai/base/migrations/0* 8 | exclude django_ai/bayesian_networks/migrations/0* 9 | exclude django_ai/supervised_learning/migrations/0* 10 | exclude django_ai/systems/spam_filtering/migrations/0* 11 | exclude django_ai/media/django_ai/bayesian_networks/* -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean-pyc clean-build docs help 2 | .DEFAULT_GOAL := help 3 | define BROWSER_PYSCRIPT 4 | import os, webbrowser, sys 5 | try: 6 | from urllib import pathname2url 7 | except: 8 | from urllib.request import pathname2url 9 | 10 | webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) 11 | endef 12 | export BROWSER_PYSCRIPT 13 | BROWSER := python -c "$$BROWSER_PYSCRIPT" 14 | 15 | help: 16 | @perl -nle'print $& if m{^[a-zA-Z_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-25s\033[0m %s\n", $$1, $$2}' 17 | 18 | clean: clean-build clean-pyc 19 | 20 | clean-build: ## remove build artifacts 21 | rm -fr build/ 22 | rm -fr dist/ 23 | rm -fr *.egg-info 24 | 25 | clean-pyc: ## remove Python file artifacts 26 | find . -name '*.pyc' -exec rm -f {} + 27 | find . -name '*.pyo' -exec rm -f {} + 28 | find . -name '*~' -exec rm -f {} + 29 | 30 | lint: ## check style with flake8 31 | flake8 django_ai tests 32 | 33 | test: ## run tests quickly with the default Python 34 | python runtests.py tests 35 | 36 | test-all: ## run tests on every Python version with tox 37 | tox 38 | 39 | coverage: ## check code coverage quickly with the default Python 40 | coverage run --source django_ai runtests.py tests 41 | coverage report -m 42 | coverage html 43 | open htmlcov/index.html 44 | 45 | docs: ## generate Sphinx HTML documentation, including API docs 46 | rm -f docs/django-ai.rst 47 | rm -f docs/modules.rst 48 | sphinx-apidoc -o docs/ django_ai 49 | $(MAKE) -C docs clean 50 | $(MAKE) -C docs html 51 | $(BROWSER) docs/_build/html/index.html 52 | 53 | release: clean ## package and upload a release 54 | python setup.py sdist upload 55 | python setup.py bdist_wheel upload 56 | 57 | sdist: clean ## package 58 | python setup.py sdist 59 | ls -l dist 60 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ========= 2 | django-ai 3 | ========= 4 | 5 | .. image:: https://badge.fury.io/py/django-ai.svg 6 | :target: https://badge.fury.io/py/django-ai 7 | 8 | .. image:: https://travis-ci.org/math-a3k/django-ai.svg?branch=master 9 | :target: https://travis-ci.org/math-a3k/django-ai 10 | 11 | .. image:: https://codecov.io/gh/math-a3k/django-ai/branch/master/graph/badge.svg 12 | :target: https://codecov.io/gh/math-a3k/django-ai 13 | 14 | Artificial Intelligence for Django 15 | ================================== 16 | 17 | ``django-ai`` is a collection of apps for integrating statistical models into your 18 | Django project so you can implement machine learning conveniently. 19 | 20 | It integrates several libraries and engines providing your Django app with a set of 21 | tools so you can leverage the data generated in your project. 22 | 23 | .. image:: http://django-ai.readthedocs.io/en/latest/_images/django_ai.jpg 24 | :target: https://django-ai.readthedocs.io/en/latest/introduction.html 25 | 26 | Documentation 27 | ------------- 28 | 29 | The full documentation is at https://django-ai.readthedocs.io or the `/docs` directory for offline reading. 30 | 31 | Features 32 | -------- 33 | 34 | * `Bayesian Networks `_: Integrate Bayesian Networks through your models using the `BayesPy framework `_. 35 | * `Spam Filtering `_: Integrate Spam Filters to your Django project using the `scikit-learn framework `_. 36 | 37 | See the `Introduction `_ section in the documentation for more information. 38 | 39 | Communication Channels 40 | ---------------------- 41 | 42 | * Mailing List: django-ai@googlegroups.com 43 | * Chat: https://gitter.im/django-ai/django-ai 44 | * GitHub: https://github.com/math-a3k/django-ai/issues 45 | * Stack-Overflow: https://stackoverflow.com/questions/tagged/django-ai 46 | * AI Stack Exchange: https://ai.stackexchange.com/questions/tagged/django-ai 47 | 48 | Quickstart 49 | ---------- 50 | 51 | The easiest way of trying `django-ai` is inside its package: 52 | 53 | 1. Create a virtual environment and activate it:: 54 | 55 | python3 -m venv django-ai_env 56 | source django-ai_env/bin/activate 57 | 58 | 2. Upgrade ``pip`` and install ``django-ai``:: 59 | 60 | (django-ai_env) pip install --upgrade pip 61 | (django-ai_env) pip install django-ai 62 | 63 | 3. Change into the `django-ai` directory, i.e.:: 64 | 65 | (django-ai_env) cd django-ai_env/lib/python3.5/site-packages/django_ai 66 | 67 | 4. Create the migrations for the dependencies and apply them:: 68 | 69 | python manage.py makemigrations 70 | python manage.py migrate 71 | 72 | 5. Create a superuser:: 73 | 74 | python manage.py createsuperuser 75 | 76 | 6. Start the development server and visit http://127.0.0.1:8000/admin/, look at the examples and start creating your statistical models:: 77 | 78 | python manage.py runserver 79 | 80 | You can also clone it from the repository and install the requirements in a virtualenv:: 81 | 82 | git clone git@github.com:math-a3k/django-ai.git 83 | 84 | and following the previous steps, install the requirements - ``pip install -r requirements.txt`` - in a virtual environment instead of the package. 85 | 86 | For installing it in your project, please refer `here `_. 87 | 88 | 89 | Running Tests 90 | ------------- 91 | 92 | Does the code actually work? 93 | 94 | :: 95 | 96 | source /bin/activate 97 | (myenv) $ pip install -r requirements_test.txt 98 | (myenv) $ PYTHONHASHSEED=0 python runtests.py 99 | -------------------------------------------------------------------------------- /django_ai.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/math-a3k/django-ai/c2abc027bb47eb4a518b8daa8c0e73b2175cc59f/django_ai.jpg -------------------------------------------------------------------------------- /django_ai/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.0.2.1' 2 | -------------------------------------------------------------------------------- /django_ai/apps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 2 | from django.apps import AppConfig 3 | 4 | 5 | class DjangoAIConfig(AppConfig): 6 | name = 'django_ai' 7 | -------------------------------------------------------------------------------- /django_ai/base/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/math-a3k/django-ai/c2abc027bb47eb4a518b8daa8c0e73b2175cc59f/django_ai/base/__init__.py -------------------------------------------------------------------------------- /django_ai/base/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.contrib.contenttypes.admin import GenericTabularInline 4 | 5 | from .models import DataColumn 6 | 7 | 8 | class DataColumnInline(GenericTabularInline): # pragma: no cover 9 | model = DataColumn 10 | # sortable_field_name = "position" 11 | fields = ["ref_model", "ref_column", "position"] 12 | ct_field = 'content_type' 13 | ct_fk_field = 'object_id' 14 | extra = 1 15 | -------------------------------------------------------------------------------- /django_ai/base/apps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | 5 | from django.apps import AppConfig 6 | 7 | 8 | class BaseConfig(AppConfig): 9 | name = 'base' 10 | verbose_name = '[django-ai] Base' 11 | 12 | 13 | if 'DJANGO_TEST' in os.environ: 14 | BaseConfig.name = 'django_ai.base' 15 | -------------------------------------------------------------------------------- /django_ai/base/templates/base/admin/mixed_inlines_change_form.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/change_form.html" %} 2 | {% load admin_extras static i18n %} 3 | {# https://github.com/dezede/dezede/blob/master/libretto/templates/admin/libretto/change_form.html #} 4 | 5 | {# Here we render mixed normal and inline fieldsets #} 6 | {% block field_sets %} 7 | {% get_fieldsets_and_inlines as fieldsets_and_inlines %} 8 | {% for type, fieldset_or_inline in fieldsets_and_inlines %} 9 | {% if type == 'f' %} 10 | {% with fieldset=fieldset_or_inline %} 11 | {% include "admin/includes/fieldset.html" %} 12 | {% endwith %} 13 | {% elif type == 'i' %} 14 | {% with inline_admin_formset=fieldset_or_inline %} 15 | {% include inline_admin_formset.opts.template %} 16 | {% endwith %} 17 | {% endif %} 18 | {% endfor %} 19 | {% endblock %} 20 | 21 | {# And there we remove standard inline rendering #} 22 | {% block inline_field_sets %} 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /django_ai/base/templates/base/snippets/ai_actions.html: -------------------------------------------------------------------------------- 1 | {% load static i18n admin_extras %} 2 | 3 |

4 |

{% trans "Actions" %}

5 |
    6 |
  • {% trans "Run inference" %}
  • 7 |
  • {% trans "Reset inference" %}
  • 8 |
  • {% trans "Re-initialize the random number generator" %}
  • 9 |

    -------------------------------------------------------------------------------- /django_ai/base/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/math-a3k/django-ai/c2abc027bb47eb4a518b8daa8c0e73b2175cc59f/django_ai/base/templatetags/__init__.py -------------------------------------------------------------------------------- /django_ai/base/templatetags/admin_extras.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.template import Library 4 | from django.contrib.contenttypes.models import ContentType 5 | from django.urls import reverse 6 | 7 | 8 | register = Library() 9 | 10 | 11 | @register.simple_tag(takes_context=True) 12 | def get_fieldsets_and_inlines(context): 13 | """ 14 | https://github.com/dezede/dezede/blob/master/libretto/ 15 | templatetags/admin_extras.py 16 | """ 17 | adminform = context['adminform'] 18 | model_admin = adminform.model_admin 19 | adminform = list(adminform) 20 | inlines = list(context['inline_admin_formsets']) 21 | 22 | fieldsets_and_inlines = [] 23 | for choice in getattr(model_admin, 'fieldsets_and_inlines_order', ()): 24 | if choice == 'f': 25 | if adminform: 26 | fieldsets_and_inlines.append(('f', adminform.pop(0))) 27 | elif choice == 'i': 28 | if inlines: 29 | fieldsets_and_inlines.append(('i', inlines.pop(0))) 30 | 31 | for fieldset in adminform: 32 | fieldsets_and_inlines.append(('f', fieldset)) 33 | for inline in inlines: 34 | fieldsets_and_inlines.append(('i', inline)) 35 | 36 | return(fieldsets_and_inlines) 37 | 38 | 39 | @register.filter 40 | def get_item(dictionary, key): 41 | return dictionary.get(key) 42 | 43 | 44 | @register.simple_tag 45 | def action_url(action, action_object=None): 46 | # import ipdb; ipdb.set_trace() 47 | if action_object: 48 | ct = ContentType.objects.get_for_model(action_object) 49 | return( 50 | reverse('run-action', kwargs={ 51 | "action": action, "content_type": ct.model, 52 | "object_id": action_object.id} 53 | ) 54 | ) 55 | else: 56 | return( 57 | reverse('run-action', kwargs={"action": action}) 58 | ) 59 | 60 | 61 | @register.inclusion_tag('base/snippets/ai_actions.html', takes_context=True) 62 | def ai_actions(context): 63 | return({"original": context['original']}) 64 | -------------------------------------------------------------------------------- /django_ai/base/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | 5 | from django.conf.urls import (url, include) 6 | 7 | from . import views 8 | 9 | examples_urls = 'examples.urls' 10 | if 'DJANGO_TEST' in os.environ: 11 | examples_urls = 'django_ai.examples.urls' 12 | 13 | urlpatterns = [ 14 | url((r'^run-action/(?P[\w_]+)/' 15 | r'(?P[\w_]+)/' 16 | r'(?P[0-9]+)$'), 17 | views.RunActionView.as_view(), 18 | name="run-action"), 19 | url((r'^run-action/(?P[\w_]+)$'), 20 | views.RunActionView.as_view(), 21 | name="run-action"), 22 | # Examples 23 | url(r'^examples/', include(examples_urls)), 24 | ] 25 | -------------------------------------------------------------------------------- /django_ai/base/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.contrib.contenttypes.models import ContentType 4 | 5 | 6 | def get_model(app_model_str): 7 | app, model = app_model_str.split(".") 8 | model_class = ContentType.objects.get( 9 | app_label=app, 10 | model=model.lower() 11 | ).model_class() 12 | return(model_class) 13 | -------------------------------------------------------------------------------- /django_ai/base/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import inspect 4 | import random 5 | import numpy as np 6 | 7 | from django.contrib import messages 8 | from django.views.generic import RedirectView 9 | from django.contrib.contenttypes.models import ContentType 10 | from django.http import Http404 11 | from django.contrib.auth.mixins import UserPassesTestMixin 12 | 13 | 14 | class RunActionView(UserPassesTestMixin, RedirectView): 15 | """ 16 | Runs common Actions for Systems and Techniques 17 | """ 18 | permanent = False 19 | #: Available Actions 20 | ACTIONS = { 21 | "perform_inference": { 22 | "type": "object", 23 | "str": "PERFORMING INFERENCE", 24 | "method": "perform_inference", 25 | "kwargs": { 26 | "recalculate": True 27 | }, 28 | }, 29 | "reset_inference": { 30 | "type": "object", 31 | "str": "RESETING INFERENCE", 32 | "method": "reset_inference", 33 | "kwargs": {}, 34 | }, 35 | "reinitialize_rng": { 36 | "type": "general", 37 | "str": "REINITIALIZING RNG", 38 | "method": "action_reinitialize_rng", 39 | "kwargs": {}, 40 | } 41 | } 42 | 43 | def test_func(self): 44 | return(self.request.user.is_superuser) 45 | 46 | def action_reinitialize_rng(self): 47 | """ 48 | Reinitialize both generators 49 | """ 50 | random.seed() 51 | np.random.seed() 52 | 53 | def get_ct_object(self, content_type, object_id): 54 | ct = ContentType.objects.get(model=content_type) 55 | return(ct.model_class().objects.get(id=object_id)) 56 | 57 | def run_action(self, action, action_object=None): 58 | try: 59 | if action_object: 60 | action_method = getattr(action_object, action['method']) 61 | else: 62 | action_method = getattr(self, action['method']) 63 | action_method(**action['kwargs']) 64 | messages.success(self.request, 65 | "SUCCESS AT {}".format(action['str'])) 66 | except Exception as e: 67 | msg = e.args[0] 68 | frm = inspect.trace()[-1] 69 | mod = inspect.getmodule(frm[0]) 70 | modname = mod.__name__ if mod else frm[1] 71 | messages.error(self.request, 72 | "ERROR WHILE {}: [{}] {}".format( 73 | action['str'], modname, str(msg))) 74 | 75 | def get_redirect_url(self, *args, **kwargs): 76 | if kwargs['action'] not in self.ACTIONS: 77 | raise Http404("Action not Found") 78 | if self.ACTIONS[kwargs['action']]["type"] == 'object': 79 | action_object = self.get_ct_object(kwargs['content_type'], 80 | kwargs['object_id']) 81 | else: 82 | action_object = None 83 | self.run_action(self.ACTIONS[kwargs['action']], action_object) 84 | return(self.request.META.get('HTTP_REFERER', '/')) 85 | -------------------------------------------------------------------------------- /django_ai/bayesian_networks/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | default_app_config = 'bayesian_networks.apps.BayesianNetworksConfig' 4 | -------------------------------------------------------------------------------- /django_ai/bayesian_networks/admin.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | from django.contrib import admin 4 | from nested_admin import (NestedModelAdmin, NestedStackedInline, 5 | NestedTabularInline) 6 | 7 | from .models import (BayesianNetwork, BayesianNetworkNode, 8 | BayesianNetworkNodeColumn, BayesianNetworkEdge) 9 | 10 | 11 | class BayesianNetworkNodeColumnInline(NestedTabularInline): 12 | model = BayesianNetworkNodeColumn 13 | sortable_field_name = "position" 14 | fields = ["ref_model", "ref_column", "position"] 15 | extra = 1 16 | 17 | 18 | class BayesianNetworkNodeInline(NestedStackedInline): 19 | model = BayesianNetworkNode 20 | extra = 1 21 | inlines = [BayesianNetworkNodeColumnInline, ] 22 | fieldsets = ( 23 | (None, { 24 | 'fields': ('name', 'node_type',) 25 | }), 26 | ("Stochastic Type", { 27 | 'fields': (('distribution', 'distribution_params'), 28 | 'is_observable', ), 29 | }), 30 | ("Deterministic Type", { 31 | 'fields': (('deterministic', 'deterministic_params'), ), 32 | }), 33 | ("Visualization", { 34 | 'classes': ('collapse',), 35 | 'fields': (('graph_interval', 'image'), ), 36 | }), 37 | ("Timestamps", { 38 | 'classes': ('collapse',), 39 | 'fields': (('engine_object_timestamp', 40 | 'engine_inferred_object_timestamp'), ), 41 | }), 42 | ) 43 | 44 | class Media: 45 | css = { 46 | 'all': ('/static/css/admin/bayesian_networks.css',) 47 | } 48 | 49 | 50 | class BayesianNetworkEdgeInline(NestedTabularInline): 51 | model = BayesianNetworkEdge 52 | extra = 1 53 | 54 | def formfield_for_foreignkey(self, db_field, 55 | request=None, **kwargs): # pragma: no cover 56 | field = super(BayesianNetworkEdgeInline, self)\ 57 | .formfield_for_foreignkey(db_field, request, **kwargs) 58 | # Display only Nodes from the Network or None 59 | if db_field.name in ['child', 'parent']: 60 | if request._obj_ is not None: 61 | field.queryset = field.queryset.filter(network=request._obj_) 62 | else: 63 | field.queryset = field.queryset.none() 64 | return field 65 | 66 | 67 | @admin.register(BayesianNetwork) 68 | class BayesianNetworkAdmin(NestedModelAdmin): 69 | fieldsets = ( 70 | (None, { 71 | 'fields': ('name', 'network_type', 'results_storage') 72 | }), 73 | ("Miscellanous", { 74 | 'classes': ('collapse',), 75 | 'fields': ( 76 | ('engine_meta_iterations', 'engine_iterations'), 77 | ('counter', 'counter_threshold', 'threshold_actions'), 78 | ('engine_object_timestamp', 'image'), 79 | 'metadata', 80 | ), 81 | }), 82 | ) 83 | inlines = [ 84 | BayesianNetworkNodeInline, 85 | BayesianNetworkEdgeInline, 86 | ] 87 | 88 | def get_form(self, request, obj=None, **kwargs): # pragma: no cover 89 | # Save obj reference in the request for future processing in Inline 90 | request._obj_ = obj 91 | form = super(BayesianNetworkAdmin, self).get_form(request, obj, 92 | **kwargs) 93 | form.base_fields["metadata"].widget.attrs["disabled"] = "disabled" 94 | return(form) 95 | 96 | # @admin.register(BayesianNetworkNode) 97 | # class BayesianNetworkNodeAdmin(admin.ModelAdmin): 98 | # pass 99 | 100 | 101 | # @admin.register(BayesianNetworkEdge) 102 | # class BayesianNetworkNodeEdge(admin.ModelAdmin): 103 | # pass 104 | -------------------------------------------------------------------------------- /django_ai/bayesian_networks/apps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | 5 | from django.apps import AppConfig 6 | 7 | 8 | class BayesianNetworksConfig(AppConfig): 9 | name = 'bayesian_networks' 10 | verbose_name = '[django-ai] Bayesian Networks' 11 | 12 | 13 | if 'DJANGO_TEST' in os.environ: 14 | BayesianNetworksConfig.name = 'django_ai.bayesian_networks' 15 | -------------------------------------------------------------------------------- /django_ai/bayesian_networks/bayespy_constants.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | DIST_GAUSSIAN = "Gaussian" # Gaussian(mu, Lambda, \*\*kwargs) 4 | DIST_GAUSSIAN_ARD = "GaussianARD" # GaussianARD(mu, alpha[, ndim, shape]) 5 | DIST_GAMMA = "Gamma" # Gamma(a, b, **kwargs) 6 | DIST_WISHART = "Wishart" # Wishart(n, V, **kwargs) 7 | DIST_EXPONENTIAL = "Exponential" # Exponential(l, **kwargs) 8 | DIST_GAUSSIAN_GAMMA = "GaussianGamma" # GaussianGamma(*args, **kwargs) 9 | DIST_GAUSSIAN_WISHART = "GaussianWishart" # GaussianWishart(*args, **kw) 10 | DIST_BERNOULLI = "Bernoulli" # Bernoulli(p, **kwargs) 11 | DIST_BINOMIAL = "Binomial" # Binomial(n, p, **kwargs) 12 | DIST_CATEGORICAL = "Categorical" # Categorical(p, **kwargs) 13 | DIST_MULTINOMIAL = "Multinomial" # Multinomial(n, p, **kwargs) 14 | DIST_POISON = "Poisson" # Poisson(l, **kwargs) 15 | DIST_BETA = "Beta" # Beta(alpha, **kwargs) 16 | DIST_DIRICHLET = "Dirichlet" # Dirichlet(*args, **kwargs) 17 | # Other Stochastics Nodes (like point estimators) - Should be Stoch Type? 18 | DIST_MIXTURE = "Mixture" # Mixture(z, node_class, *params[, cluster_plat]) 19 | DIST_MAXIMUMLIKELIHOOD = "MaximumLikelihood" # MaximumLikelihood(arr[, r]) 20 | DIST_CONCENTRATION = "Concentration" # Concentration(D[, regularization]) 21 | DIST_GAMMA_SHAPE = "GammaShape" # GammaShape(**kwargs) 22 | DISTRIBUTION_CHOICES = ( 23 | (DIST_GAUSSIAN, DIST_GAUSSIAN), 24 | (DIST_GAUSSIAN_ARD, DIST_GAUSSIAN_ARD), 25 | (DIST_GAMMA, DIST_GAMMA), 26 | (DIST_WISHART, DIST_WISHART), 27 | (DIST_EXPONENTIAL, DIST_EXPONENTIAL), 28 | (DIST_GAUSSIAN_GAMMA, DIST_GAUSSIAN_GAMMA), 29 | (DIST_GAUSSIAN_WISHART, DIST_GAUSSIAN_WISHART), 30 | (DIST_BERNOULLI, DIST_BERNOULLI), 31 | (DIST_BINOMIAL, DIST_BINOMIAL), 32 | (DIST_CATEGORICAL, DIST_CATEGORICAL), 33 | (DIST_MULTINOMIAL, DIST_MULTINOMIAL), 34 | (DIST_POISON, DIST_POISON), 35 | (DIST_BETA, DIST_BETA), 36 | (DIST_DIRICHLET, DIST_DIRICHLET), 37 | (DIST_MIXTURE, DIST_MIXTURE), 38 | (DIST_MAXIMUMLIKELIHOOD, DIST_MAXIMUMLIKELIHOOD), 39 | (DIST_CONCENTRATION, DIST_CONCENTRATION), 40 | (DIST_GAMMA_SHAPE, DIST_GAMMA_SHAPE), 41 | ) 42 | DET_DOT = "Dot" # Dot(*args, **kwargs) 43 | DET_SUM_MULTIPLY = "SumMultiply" # SumMultiply(*args[, iterator_axis]) 44 | DET_ADD = "Add" # Add(*nodes, **kwargs) 45 | DET_GATE = "Gate" # Gate(Z, X[, gated_plate, moments]) 46 | DET_TAKE = "Take" # Take(node, indices[, plate_axis]) 47 | DET_FUNCTION = "Function" # Function(function, *nodes_gradients[, shape]) 48 | DET_CONCAT_GAUSSIAN = "ConcatGaussian" # ConcatGaussian(*nodes, **kwargs) 49 | DET_CHOOSE = "Choose" # Choose(z, *nodes) 50 | DETERMINISTIC_CHOICES = ( 51 | (DET_DOT, DET_DOT), 52 | (DET_SUM_MULTIPLY, DET_SUM_MULTIPLY), 53 | (DET_ADD, DET_ADD), 54 | (DET_GATE, DET_GATE), 55 | (DET_TAKE, DET_TAKE), 56 | (DET_FUNCTION, DET_FUNCTION), 57 | (DET_CONCAT_GAUSSIAN, DET_CONCAT_GAUSSIAN), 58 | (DET_CHOOSE, DET_CHOOSE), 59 | ) 60 | -------------------------------------------------------------------------------- /django_ai/bayesian_networks/static/css/admin/bayesian_networks.css: -------------------------------------------------------------------------------- 1 | 2 | [id^=nodes-] h3 { 3 | background-color: #DA6666; 4 | color: #FFFFFF; 5 | } 6 | 7 | .cm_tables { 8 | display: table; 9 | } 10 | 11 | .cm_tables li { 12 | float: left; 13 | margin-left: 15px; 14 | margin-right: 10px; 15 | } 16 | -------------------------------------------------------------------------------- /django_ai/bayesian_networks/static/js/admin/bayesian_networks.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 3 | network_type_select = $("select[name='network_type']"); 4 | storage_results_row = $("div[class='form-row field-results_storage']"); 5 | network_type_select.change(function() { 6 | select = $(this); 7 | 8 | if (select.val() == '1') { 9 | // Clustering Type 10 | storage_results_row.show(); 11 | } else { 12 | storage_results_row.hide(); 13 | } 14 | }); 15 | network_type_select.change(); 16 | 17 | node_type_selects = $("select[name$='node_type']"); 18 | node_type_selects.change(function() { 19 | select = $(this); 20 | select_fieldset = select.closest("fieldset"); 21 | stochastic_fieldset = select_fieldset.nextAll("fieldset:has(h2:contains('Stochastic Type'))"); 22 | deterministic_fieldset = select_fieldset.nextAll("fieldset:has(h2:contains('Deterministic Type'))"); 23 | visualization_fieldset = select_fieldset.nextAll("fieldset:has(h2:contains('Visualization'))"); 24 | 25 | if (select.val() == '0') { 26 | // Stochastic Type 27 | deterministic_fieldset.hide(); 28 | stochastic_fieldset.show(); 29 | visualization_fieldset.show(); 30 | } else { 31 | // Deterministic Type 32 | stochastic_fieldset.hide(); 33 | deterministic_fieldset.show(); 34 | visualization_fieldset.hide(); 35 | } 36 | }); 37 | node_type_selects.change(); 38 | 39 | is_observable_cbs = $("input[name$='is_observable']"); 40 | is_observable_cbs.change(function() { 41 | io_cb = $(this); 42 | io_fieldset = io_cb.closest("fieldset"); 43 | columns_inline = io_fieldset.nextAll("div[id$='columns-group']"); 44 | 45 | if (io_cb.is(':checked')) { 46 | columns_inline.show(); 47 | } else { 48 | columns_inline.hide(); 49 | } 50 | }); 51 | is_observable_cbs.change(); 52 | 53 | $(document).on('formset:added', function(event, $row, formsetName) { 54 | if (formsetName == 'nodes') { 55 | node_type_selects.change(); 56 | is_observable_cbs.change(); 57 | } 58 | }); 59 | 60 | $(document).on('formset:removed', function(event, $row, formsetName) { 61 | // Row removed, nothing to do here yet 62 | }); 63 | 64 | })(django.jQuery); 65 | -------------------------------------------------------------------------------- /django_ai/bayesian_networks/templates/admin/bayesian_networks/bayesiannetwork/change_form.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/change_form.html" %} 2 | {% load static i18n admin_extras %} 3 | 4 | {% block after_related_objects %} 5 | {% if original.pk %} 6 |

    {% trans "Bayesian Network" %}: {{ original.name }}

    7 |

    8 | {% if original.image %} 9 | 10 | {% endif %} 11 |

    12 | {% if original.is_inferred %} 13 |

    {% trans "Inference" %}

    14 | {% for node in original.nodes.all %} 15 | {% if node.image %} 16 |

    17 | 18 |

    19 | {% endif %} 20 | {% endfor %} 21 | {% if original.network_type == original.TYPE_CLUSTERING %} 22 |
      23 |
    • 24 |

      Clusters Means (Current)

      25 | {% include "./snippets/cm_table.html" with clusters_means=original.metadata.clusters_means clusters_sizes=original.metadata.clusters_sizes %} 26 |
    • 27 | {% if original.metadata.prev_clusters_means %} 28 |
    • 29 |

      Clusters Means (Previous Inference)

      30 | {% include "./snippets/cm_table.html" with clusters_means=original.metadata.prev_clusters_means clusters_sizes=original.metadata.prev_clusters_sizes %} 31 |
    • 32 | {% endif %} 33 |
    34 | {% endif %} 35 | {% endif %} 36 | {% ai_actions %} 37 | {% endif %} 38 | {% endblock %} 39 | 40 | {% block admin_change_form_document_ready %} 41 | {{ block.super }} 42 | 43 | {% endblock %} 44 | -------------------------------------------------------------------------------- /django_ai/bayesian_networks/templates/admin/bayesian_networks/bayesiannetwork/snippets/cm_table.html: -------------------------------------------------------------------------------- 1 | {% load admin_extras %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% for col in original.metadata.columns %} 9 | 10 | {% endfor %} 11 | 12 | 13 | {% for label, mean in clusters_means.items|dictsort:0 %} 14 | 15 | 16 | 17 | {% for col_val in mean %} 18 | 19 | {% endfor %} 20 | 21 | {% endfor %} 22 |
    ClusterSize{{ col }}
    {{ label }}{{ clusters_sizes|get_item:label }}{{ col_val|stringformat:".3f" }}
    23 | -------------------------------------------------------------------------------- /django_ai/bayesian_networks/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /django_ai/bayesian_networks/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.conf.urls import url 4 | 5 | from . import views 6 | 7 | urlpatterns = [ 8 | ] 9 | -------------------------------------------------------------------------------- /django_ai/bayesian_networks/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import numpy as np 4 | import pyparsing as pp 5 | from importlib import import_module 6 | 7 | from django.conf import settings 8 | 9 | 10 | # Load all the modules 11 | if hasattr(settings, "DJANGO_AI_WHITELISTED_MODULES"): 12 | allowed_modules = settings.DJANGO_AI_WHITELISTED_MODULES 13 | else: 14 | # Default modules 15 | allowed_modules = [ 16 | "numpy", 17 | "bayespy.nodes" 18 | ] 19 | # Import all the WL modules 20 | modules = {} 21 | for module in allowed_modules: 22 | modules[module] = import_module(module) 23 | 24 | 25 | def eval_function(parsed_fun): 26 | """ 27 | Eval a function without the need of sanitizing 28 | """ 29 | if parsed_fun[0] == "@": 30 | parsed_fun.pop(0) 31 | reference = True 32 | else: 33 | reference = False 34 | try: 35 | namespace, functor = parsed_fun.asList()[0].rsplit(".", 1) 36 | except Exception as e: 37 | raise ValueError("Functions must be namespaced") 38 | functor_args = parsed_fun[1:] 39 | try: 40 | mod = modules[namespace] 41 | except KeyError: 42 | raise ValueError("Module / Namespace not allowed, whitelist it first") 43 | try: 44 | method = getattr(mod, functor) 45 | if reference: 46 | return(method) 47 | else: 48 | # No support for kwargs yet 49 | return(method(*functor_args)) 50 | except Exception as e: 51 | msg = e.args[0] 52 | raise ValueError("Invalid function invocation:" + msg) 53 | 54 | 55 | def parse_node_args(args_string, flat=False): 56 | """ 57 | Parses the string intended for Node Initialization 58 | Based on https://groups.google.com/forum/#!msg/comp.lang.python/vgOWCZ7Z8Yw/ZQgDJfXtCj0J 59 | """ 60 | LPAR, RPAR, LBRACK, RBRACK, EQ, COMMA = \ 61 | map(pp.Suppress, "()[]=,") 62 | 63 | # Booleans and Keywords 64 | noneLiteral = pp.Literal("None") 65 | boolLiteral = pp.oneOf("True False") 66 | # Scalars 67 | integer = pp.Combine(pp.Optional(pp.oneOf("+ -")) + 68 | pp.Word(pp.nums)).setName("integer") 69 | real = pp.Combine(pp.Optional(pp.oneOf("+ -")) + 70 | pp.Word(pp.nums) + pp.oneOf(". e") + 71 | pp.Optional(pp.oneOf("+ -")) + 72 | pp.Optional(pp.Word(pp.nums))).setName("real") 73 | # Identifiers 74 | identifier = pp.Word(pp.alphas + "_:", pp.alphanums + "_:") 75 | funStr = pp.Forward().setResultsName("fun") 76 | # Structures 77 | listStr = pp.Forward() 78 | tupleStr = pp.Forward() 79 | 80 | listItem = (real | integer | 81 | noneLiteral | boolLiteral | 82 | pp.quotedString.setParseAction(pp.removeQuotes) | 83 | pp.Group(listStr()) | tupleStr() | identifier) 84 | 85 | funStr << (pp.Optional("@") + 86 | pp.delimitedList(identifier, delim=".", combine=True) + 87 | (LPAR + pp.Optional(pp.delimitedList(listItem)) + RPAR)) 88 | listStr << (LBRACK + pp.Optional(pp.delimitedList(listItem)) + 89 | pp.Optional(COMMA) + RBRACK) 90 | tupleStr << (LPAR + pp.Optional(pp.delimitedList(listItem)) + 91 | pp.Optional(COMMA) + RPAR) 92 | kwarg = (pp.Group(identifier("kwarg") + EQ + 93 | (pp.Group(funStr("fun")) | listItem() 94 | ).setResultsName("kwvalue"))) 95 | 96 | arg = pp.Group(funStr("fun")) | kwarg("kwarg") | listItem 97 | args = pp.delimitedList(arg) 98 | 99 | # parse actions perform parse-time conversions 100 | noneLiteral.setParseAction(lambda: None) 101 | boolLiteral.setParseAction(lambda toks: toks[0] == "True") 102 | integer .setParseAction(lambda toks: int(toks[0])) 103 | real .setParseAction(lambda toks: float(toks[0])) 104 | listStr .setParseAction(lambda toks: toks.asList()) 105 | tupleStr .setParseAction(lambda toks: tuple(toks.asList())) 106 | funStr .setParseAction(lambda toks: toks.asList()) 107 | 108 | parsedArgs = args.parseString(args_string) 109 | 110 | f_args = [] 111 | f_kwargs = {} 112 | 113 | for item in parsedArgs: 114 | if isinstance(item, pp.ParseResults): 115 | if 'kwarg' in item: 116 | if 'fun' in item.kwvalue: 117 | kw_value = eval_function(item.kwvalue) 118 | f_kwargs[item.kwarg] = kw_value 119 | else: 120 | kw = item.asList()[0] 121 | kw_val = item.asList()[1] 122 | f_kwargs[kw] = kw_val 123 | else: 124 | if 'fun' in item: 125 | f_args.append(eval_function(item)) 126 | else: 127 | f_args.append(item.asList()) 128 | else: 129 | # It is one of the "basic objects" and passed as an arg 130 | f_args.append(item) 131 | 132 | if flat: 133 | return f_args + list(f_kwargs.values()) 134 | else: 135 | return({"args": f_args, "kwargs": f_kwargs}) 136 | 137 | 138 | def mahalanobis_distance(x, y, S): 139 | """ 140 | Returns the Mahalanobis distance between x and y, given the covariance 141 | matrix S. 142 | """ 143 | S_inv = np.linalg.inv(S) 144 | dxy = np.array(x) - np.array(y) 145 | dist = np.sqrt(np.dot(np.dot(dxy.T, S_inv), dxy)) 146 | return(dist) 147 | -------------------------------------------------------------------------------- /django_ai/bayesian_networks/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /django_ai/django_ai/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/math-a3k/django-ai/c2abc027bb47eb4a518b8daa8c0e73b2175cc59f/django_ai/django_ai/__init__.py -------------------------------------------------------------------------------- /django_ai/django_ai/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for django_ai project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.11. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.11/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 | # Insert to the system path the directory of django-ai's systems apps 19 | os.sys.path.insert(0, os.path.join(BASE_DIR, 'systems')) 20 | 21 | # Quick-start development settings - unsuitable for production 22 | # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ 23 | 24 | # SECURITY WARNING: keep the secret key used in production secret! 25 | SECRET_KEY = '66666666666666666666666666666666666666666666666666' 26 | 27 | # SECURITY WARNING: don't run with debug turned on in production! 28 | DEBUG = True 29 | 30 | ALLOWED_HOSTS = [] 31 | 32 | 33 | # Application definition 34 | 35 | INSTALLED_APPS = [ 36 | 'django.contrib.admin', 37 | 'django.contrib.auth', 38 | 'django.contrib.contenttypes', 39 | 'django.contrib.sessions', 40 | 'django.contrib.messages', 41 | 'django.contrib.staticfiles', 42 | 43 | # Dependencies 44 | 'nested_admin', 45 | 46 | # django-ai 47 | 'base', 48 | 'bayesian_networks', 49 | 'supervised_learning', 50 | 'systems.spam_filtering', 51 | 'examples', 52 | ] 53 | 54 | MIDDLEWARE = [ 55 | 'django.middleware.security.SecurityMiddleware', 56 | 'django.contrib.sessions.middleware.SessionMiddleware', 57 | 'django.middleware.common.CommonMiddleware', 58 | 'django.middleware.csrf.CsrfViewMiddleware', 59 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 60 | 'django.contrib.messages.middleware.MessageMiddleware', 61 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 62 | ] 63 | 64 | ROOT_URLCONF = 'django_ai.urls' 65 | 66 | TEMPLATES = [ 67 | { 68 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 69 | 'DIRS': [], 70 | 'APP_DIRS': True, 71 | 'OPTIONS': { 72 | 'context_processors': [ 73 | 'django.template.context_processors.debug', 74 | 'django.template.context_processors.request', 75 | 'django.contrib.auth.context_processors.auth', 76 | 'django.contrib.messages.context_processors.messages', 77 | ], 78 | }, 79 | }, 80 | ] 81 | 82 | WSGI_APPLICATION = 'django_ai.wsgi.application' 83 | 84 | 85 | # Database 86 | # https://docs.djangoproject.com/en/1.11/ref/settings/#databases 87 | 88 | DATABASES = { 89 | 'default': { 90 | 'ENGINE': 'django.db.backends.sqlite3', 91 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 92 | } 93 | } 94 | 95 | 96 | # Password validation 97 | # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators 98 | 99 | AUTH_PASSWORD_VALIDATORS = [ 100 | { 101 | 'NAME': ('django.contrib.auth.password_validation.' 102 | 'UserAttributeSimilarityValidator'), 103 | }, 104 | { 105 | 'NAME': ('django.contrib.auth.password_validation.' 106 | 'MinimumLengthValidator'), 107 | }, 108 | { 109 | 'NAME': ('django.contrib.auth.password_validation.' 110 | 'CommonPasswordValidator'), 111 | }, 112 | { 113 | 'NAME': ('django.contrib.auth.password_validation.' 114 | 'NumericPasswordValidator'), 115 | }, 116 | ] 117 | 118 | 119 | # Internationalization 120 | # https://docs.djangoproject.com/en/1.11/topics/i18n/ 121 | 122 | LANGUAGE_CODE = 'en-us' 123 | 124 | TIME_ZONE = 'UTC' 125 | 126 | USE_I18N = True 127 | 128 | USE_L10N = True 129 | 130 | USE_TZ = True 131 | 132 | 133 | # Static files (CSS, JavaScript, Images) 134 | # https://docs.djangoproject.com/en/1.11/howto/static-files/ 135 | 136 | STATIC_URL = '/static/' 137 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 138 | MEDIA_URL = '/media/' 139 | -------------------------------------------------------------------------------- /django_ai/django_ai/urls.py: -------------------------------------------------------------------------------- 1 | """django_ai URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.11/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 | 17 | from django.conf.urls import (url, include) 18 | from django.conf import settings 19 | from django.conf.urls.static import static 20 | from django.contrib import admin 21 | 22 | urlpatterns = [ 23 | url(r'^admin/', admin.site.urls), 24 | url(r'^nested_admin/', include('nested_admin.urls')), 25 | url(r'^django-ai/', include('base.urls')), 26 | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 27 | -------------------------------------------------------------------------------- /django_ai/django_ai/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for django_ai 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.11/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", "django_ai.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /django_ai/examples/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | default_app_config = "examples.apps.ExamplesConfig" 4 | -------------------------------------------------------------------------------- /django_ai/examples/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.contrib import admin 4 | 5 | from .models import (UserInfo, CommentOfMySite, SFPTEnron, SFPTYoutube) 6 | 7 | UserInfo.get_sex_display.short_description = "Sex" 8 | 9 | 10 | @admin.register(UserInfo) 11 | class UserInfoAdmin(admin.ModelAdmin): 12 | list_display = ['id', 'age', 'get_sex_display', 'avg1', 13 | 'avg_time_pages', 'visits_pages', 'avg_time_pages_a', 14 | 'visits_pages_a', 'cluster_1'] 15 | fieldsets = ( 16 | (None, { 17 | 'fields': ('age', 'sex',) 18 | }), 19 | (None, { 20 | 'fields': ('avg1',) 21 | }), 22 | ("Pages of Type X", { 23 | 'fields': ( 24 | ('visits_pages_a', 'avg_time_pages_a', ), 25 | ('visits_pages_b', 'avg_time_pages_b', ), 26 | ('visits_pages_c', 'avg_time_pages_c', ), 27 | ('visits_pages_d', 'avg_time_pages_d', ), 28 | ('visits_pages_e', 'avg_time_pages_e', ), 29 | ('visits_pages_f', 'avg_time_pages_f', ), 30 | ('visits_pages_g', 'avg_time_pages_g', ), 31 | ('visits_pages_h', 'avg_time_pages_h', ), 32 | ('visits_pages_i', 'avg_time_pages_i', ), 33 | ('visits_pages_j', 'avg_time_pages_j', ), 34 | ), 35 | }), 36 | ("Visits and Pages (General)", { 37 | 'fields': ( 38 | ('visits_pages', 'avg_time_pages', ), 39 | ), 40 | }), 41 | ) 42 | 43 | 44 | @admin.register(CommentOfMySite) 45 | class CommentsOfMySiteAdmin(admin.ModelAdmin): 46 | list_filter = ['is_spam', 'is_revised', ] 47 | 48 | 49 | @admin.register(SFPTEnron) 50 | class SFPTEnronAdmin(admin.ModelAdmin): 51 | list_display = ['content', 'is_spam', ] 52 | list_filter = ['is_spam', ] 53 | 54 | 55 | @admin.register(SFPTYoutube) 56 | class SFPTYoutubeAdmin(admin.ModelAdmin): 57 | list_display = ['content', 'is_spam', ] 58 | list_filter = ['is_spam', ] 59 | -------------------------------------------------------------------------------- /django_ai/examples/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ExamplesConfig(AppConfig): 5 | name = 'examples' 6 | verbose_name = '[django-ai] Examples' 7 | -------------------------------------------------------------------------------- /django_ai/examples/forms.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django import forms 4 | 5 | from .models import CommentOfMySite 6 | 7 | 8 | class CommentOfMySiteForm(forms.ModelForm): 9 | 10 | class Meta: 11 | model = CommentOfMySite 12 | fields = ('comment', 'user_id', ) 13 | widgets = { 14 | 'user_id': forms.HiddenInput(), 15 | 'comment': forms.Textarea(attrs={'class': 'materialize-textarea'}) 16 | } 17 | -------------------------------------------------------------------------------- /django_ai/examples/metrics.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Metrics to be evaulated on the Metrics Pipeline 5 | ----------------------------------------------- 6 | 7 | Any function defined or imported here whose name starts with "metric_" will be 8 | executed by the Metrics Pipeline with the data as its first positional 9 | argument. 10 | 11 | A metric function should pass if the data does not corresponds to what it is 12 | intended to record. 13 | """ 14 | import os 15 | 16 | if 'DJANGO_TEST' in os.environ: 17 | from django_ai.examples.models import UserInfo 18 | else: 19 | from examples.models import UserInfo 20 | 21 | 22 | def metric_visits_and_avg_time_page_X(data): 23 | """ 24 | Updates the average time on pages of type X and its amount of visits 25 | """ 26 | if data["metric"] == "time_spent" and data["page_type"]: 27 | ptype = data["page_type"].lower() 28 | if ptype not in "abcdefghij": 29 | return(False) # Ignore the data 30 | ui = UserInfo.objects.get(id=data["user_id"]) 31 | avg_time_pages_X = "avg_time_pages_" + ptype 32 | visits_pages_X = "visits_pages_" + ptype 33 | # 34 | time_spent = float(data["time_spent"]) 35 | n = getattr(ui, visits_pages_X) 36 | avg_time = getattr(ui, avg_time_pages_X) 37 | updated_avg_time = (avg_time * (n / (n + 1)) + (time_spent / (n + 1))) 38 | setattr(ui, avg_time_pages_X, updated_avg_time) 39 | setattr(ui, visits_pages_X, n + 1) 40 | ui.save() 41 | return(True) 42 | else: 43 | pass 44 | 45 | 46 | def metric_visits_and_avg_time_on_pages(data): 47 | """ 48 | Updates the average time on pages and its amount of visits 49 | """ 50 | if data["metric"] == "time_spent": 51 | ui = UserInfo.objects.get(id=data["user_id"]) 52 | time_spent = float(data["time_spent"]) 53 | n = ui.visits_pages 54 | ui.avg_time_pages = (ui.avg_time_pages * (n / (n + 1)) + 55 | (time_spent / (n + 1))) 56 | ui.visits_pages = n + 1 57 | ui.save() 58 | return(True) 59 | else: 60 | pass 61 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11 on 2017-04-26 22:59 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='UserInfo', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('age', models.IntegerField(verbose_name='Age')), 21 | ('sex', models.CharField(choices=[(0, 'M'), (1, 'F')], max_length=1, verbose_name='Sex')), 22 | ], 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/0002_student_avg1.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11 on 2017-05-02 23:11 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('examples', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='userinfo', 17 | name='avg1', 18 | field=models.FloatField(blank=True, null=True, verbose_name='Average 1'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/0003_populate_userinfo.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11 on 2017-05-02 23:12 3 | from __future__ import unicode_literals 4 | import numpy as np 5 | 6 | from django.db import migrations 7 | from django.conf import settings 8 | 9 | 10 | def populate_userinfos(apps, schema_editor): 11 | UserInfo = apps.get_model("examples", "UserInfo") 12 | # Use a fixed seed for generate content 13 | np.random.seed(123456) 14 | # Size of table 15 | table_size = getattr(settings, "DJANGO_AI_EXAMPLES_USERINFO_SIZE", 200) 16 | print(" (Table size is {})".format(table_size), end="") 17 | # Sex is ~ 70% F (0) / 30% M (1) 18 | sex = np.random.binomial(1, 0.7, table_size) # 200 Bernoullies :) 19 | # Age is around 30, mostly between 25 and 35 20 | age = np.floor(np.random.normal(30, 2, size=(table_size,))) 21 | # Average 1 is a metric normally distributed around 10 with a std dev of 5 22 | avg1 = np.random.normal(10, 5, size=(table_size,)) 23 | # Create the objects in the Model 24 | uis = [] 25 | for i in range(0, table_size): 26 | uis.append(UserInfo(age=age[i], sex=sex[i], avg1=avg1[i])) 27 | UserInfo.objects.bulk_create(uis) 28 | 29 | 30 | def unpopuplate_userinfos(apps, schema_editor): 31 | UserInfo = apps.get_model("examples", "UserInfo") 32 | UserInfo.objects.all().delete() 33 | 34 | 35 | class Migration(migrations.Migration): 36 | 37 | dependencies = [ 38 | ('examples', '0002_student_avg1'), 39 | ] 40 | 41 | operations = [ 42 | migrations.RunPython(populate_userinfos, 43 | unpopuplate_userinfos), 44 | ] 45 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/0004_bn_example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11 on 2017-09-02 20:29 3 | from __future__ import unicode_literals 4 | import os 5 | 6 | from graphviz import Digraph 7 | 8 | from django.db import migrations 9 | from django.core.files.base import ContentFile 10 | from django.contrib.contenttypes.management import create_contenttypes 11 | 12 | from bayesian_networks.bayespy_constants import ( 13 | DIST_GAUSSIAN_ARD, DIST_GAMMA) 14 | from bayesian_networks.models import BayesianNetworkNode as BNN 15 | 16 | 17 | def generate_bn_image(bn): 18 | """ 19 | Auxiliary function for generating the image of a BN, as model 20 | methods are not available in migrations. 21 | """ 22 | dot = Digraph(comment=bn.name) 23 | nodes = bn.nodes.all() 24 | for node in nodes: 25 | dot.node(name=node.name, label=node.name) 26 | edges = bn.edges.all() 27 | for edge in edges: 28 | dot.edge(str(edge.parent.name), 29 | str(edge.child.name)) 30 | dot.format = "png" 31 | contentfile = ContentFile(dot.pipe()) 32 | image_name = "{0}/{1}".format( 33 | os.path.join("django_ai", 34 | "bayesian_networks"), 35 | bn.name + ".png") 36 | bn.image.save(image_name, contentfile) 37 | bn.save() 38 | 39 | 40 | def create_bn1_example(apps, schema_editor): 41 | """ 42 | Create a Bayesian Network from the scratch. 43 | """ 44 | # Content Types Hackery for ensuring that it exists 45 | app_config = apps.get_app_config('examples') 46 | app_config.models_module = app_config.models_module or True 47 | create_contenttypes(app_config) 48 | ## 49 | 50 | BayesianNetwork = apps.get_model("bayesian_networks", 51 | "BayesianNetwork") 52 | BayesianNetworkEdge = apps.get_model("bayesian_networks", 53 | "BayesianNetworkEdge") 54 | BayesianNetworkNode = apps.get_model("bayesian_networks", 55 | "BayesianNetworkNode") 56 | BayesianNetworkNodeColumn = apps.get_model("bayesian_networks", 57 | "BayesianNetworkNodeColumn") 58 | ContentType = apps.get_model("contenttypes", 59 | "ContentType") 60 | 61 | bn1 = BayesianNetwork(name="BN1 (Example)") 62 | bn1.save() 63 | mu = BayesianNetworkNode( 64 | network=bn1, 65 | name="mu", 66 | node_type=BNN.NODE_TYPE_STOCHASTIC, 67 | is_observable=False, 68 | distribution=DIST_GAUSSIAN_ARD, 69 | distribution_params="0, 1e-6", 70 | graph_interval="-10, 20" 71 | ) 72 | tau = BayesianNetworkNode( 73 | network=bn1, 74 | name="tau", 75 | node_type=BNN.NODE_TYPE_STOCHASTIC, 76 | is_observable=False, 77 | distribution=DIST_GAMMA, 78 | distribution_params="1e-6, 1e-6", 79 | graph_interval="1e-6, 0.1" 80 | ) 81 | ui_avg1 = BayesianNetworkNode( 82 | network=bn1, 83 | name="userinfo.avg1", 84 | node_type=BNN.NODE_TYPE_STOCHASTIC, 85 | is_observable=True, 86 | distribution=DIST_GAUSSIAN_ARD, 87 | distribution_params="mu, tau", 88 | ) 89 | mu.save() 90 | tau.save() 91 | ui_avg1.save() 92 | # 93 | ui_avg1_col = BayesianNetworkNodeColumn( 94 | node=ui_avg1, 95 | ref_model=ContentType.objects.get(model="userinfo", 96 | app_label="examples"), 97 | ref_column="avg1" 98 | ) 99 | ui_avg1_col.save() 100 | # 101 | mu_to_ui_avg1 = BayesianNetworkEdge( 102 | network=bn1, 103 | description="mu -> userinfo.avg1", 104 | parent=mu, 105 | child=ui_avg1 106 | ) 107 | tau_to_ui_avg1 = BayesianNetworkEdge( 108 | network=bn1, 109 | description="tau -> userinfo.avg1", 110 | parent=tau, 111 | child=ui_avg1 112 | ) 113 | mu_to_ui_avg1.save() 114 | tau_to_ui_avg1.save() 115 | # Generate the image 116 | generate_bn_image(bn1) 117 | 118 | 119 | def delete_bn1_example(apps, schema_editor): 120 | BayesianNetwork = apps.get_model("bayesian_networks", 121 | "BayesianNetwork") 122 | 123 | BayesianNetwork.objects.get(name="BN1 (Example)").delete() 124 | 125 | 126 | class Migration(migrations.Migration): 127 | 128 | dependencies = [ 129 | ('examples', '0003_populate_userinfo'), 130 | ] 131 | 132 | operations = [ 133 | migrations.RunPython(create_bn1_example, 134 | delete_bn1_example), 135 | ] 136 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/0005_add_avg_times_clusters.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-10-09 03:20 3 | from __future__ import unicode_literals 4 | import numpy as np 5 | 6 | from django.db import migrations, models 7 | from django.conf import settings 8 | 9 | 10 | def populate_avg_times(apps, schema_editor): 11 | UserInfo = apps.get_model("examples", "UserInfo") 12 | # Use a fixed seed for generate content 13 | np.random.seed(123456) 14 | table_size = getattr(settings, "DJANGO_AI_EXAMPLES_USERINFO_SIZE", 200) 15 | group_size = int(table_size / 4) # Equal sizes for all groups 16 | # Generate the clusters 17 | y0 = np.random.multivariate_normal([20, 20], [[2, 0], [0, 0.1]], 18 | size=group_size) 19 | y1 = np.random.multivariate_normal([20, 20], [[0.1, 0], [0, 2]], 20 | size=group_size) 21 | y2 = np.random.multivariate_normal([25, 25], [[2, -1.5], [-1.5, 2]], 22 | size=group_size) 23 | y3 = np.random.multivariate_normal([16, 16], [[0.5, 0], [0, 0.5]], 24 | size=group_size) 25 | y = np.vstack([y0, y1, y2, y3]) 26 | uis = UserInfo.objects.all() 27 | # Update the objects in the Model 28 | for index, ui in enumerate(uis): 29 | ui.avg_time_pages_a = y[index][0] 30 | ui.avg_time_logged = y[index][1] 31 | ui.save(update_fields=['avg_time_logged', 'avg_time_pages_a']) 32 | 33 | 34 | def unpopulate_avg_times(apps, schema_editor): 35 | """ 36 | This is for making the migration reversible, it doesn't do anything 37 | because the fields will be removed after by the reverse of AddField. 38 | """ 39 | pass 40 | 41 | 42 | class Migration(migrations.Migration): 43 | 44 | dependencies = [ 45 | ('examples', '0004_bn_example'), 46 | ] 47 | 48 | operations = [ 49 | migrations.AddField( 50 | model_name='userinfo', 51 | name='avg_time_pages_a', 52 | field=models.FloatField( 53 | blank=True, null=True, 54 | verbose_name='Average Time spent on Pages A'), 55 | ), 56 | migrations.AddField( 57 | model_name='userinfo', 58 | name='avg_time_logged', 59 | field=models.FloatField( 60 | blank=True, null=True, 61 | verbose_name='Average Weekly Time Logged In'), 62 | ), 63 | migrations.RunPython(populate_avg_times, unpopulate_avg_times), 64 | ] 65 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/0006_clustering_bn_example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11 on 2017-09-02 20:29 3 | from __future__ import unicode_literals 4 | import os 5 | 6 | from graphviz import Digraph 7 | 8 | from django.db import migrations 9 | from django.core.files.base import ContentFile 10 | from django.contrib.contenttypes.management import create_contenttypes 11 | 12 | from bayesian_networks.bayespy_constants import ( 13 | DIST_DIRICHLET, DIST_CATEGORICAL, DIST_GAUSSIAN, DIST_WISHART, 14 | DIST_MIXTURE) 15 | from bayesian_networks.models import BayesianNetwork as BN 16 | from bayesian_networks.models import BayesianNetworkNode as BNN 17 | 18 | 19 | def generate_bn_image(bn): 20 | """ 21 | Auxiliary function for generating the image of a BN, as model 22 | methods are not available in migrations. 23 | """ 24 | dot = Digraph(comment=bn.name) 25 | nodes = bn.nodes.all() 26 | for node in nodes: 27 | dot.node(name=node.name, label=node.name) 28 | edges = bn.edges.all() 29 | for edge in edges: 30 | dot.edge(str(edge.parent.name), 31 | str(edge.child.name)) 32 | dot.format = "png" 33 | contentfile = ContentFile(dot.pipe()) 34 | image_name = "{0}/{1}".format( 35 | os.path.join("django_ai", "bayesian_networks"), 36 | bn.name + ".png") 37 | bn.image.save(image_name, contentfile) 38 | bn.save() 39 | 40 | 41 | def create_clustering_bn_example(apps, schema_editor): 42 | """ 43 | Create a Bayesian Network from the scratch. 44 | """ 45 | # Content Types Hackery for ensuring that it exists 46 | app_config = apps.get_app_config('examples') 47 | app_config.models_module = app_config.models_module or True 48 | create_contenttypes(app_config) 49 | ## 50 | 51 | BayesianNetwork = apps.get_model( 52 | "bayesian_networks", "BayesianNetwork") 53 | BayesianNetworkEdge = apps.get_model( 54 | "bayesian_networks", "BayesianNetworkEdge") 55 | BayesianNetworkNode = apps.get_model( 56 | "bayesian_networks", "BayesianNetworkNode") 57 | BayesianNetworkNodeColumn = apps.get_model( 58 | "bayesian_networks", "BayesianNetworkNodeColumn") 59 | 60 | ContentType = apps.get_model( 61 | "contenttypes", "ContentType") 62 | 63 | bn = BayesianNetwork( 64 | name="Clustering (Example)", 65 | network_type=BN.TYPE_CLUSTERING, 66 | ) 67 | bn.save() 68 | alpha = BayesianNetworkNode( 69 | network=bn, 70 | name="alpha", 71 | node_type=BNN.NODE_TYPE_STOCHASTIC, 72 | is_observable=False, 73 | distribution=DIST_DIRICHLET, 74 | distribution_params="numpy.full(10, 1e-05)", 75 | ) 76 | Z = BayesianNetworkNode( 77 | network=bn, 78 | name="Z", 79 | node_type=BNN.NODE_TYPE_STOCHASTIC, 80 | is_observable=False, 81 | distribution=DIST_CATEGORICAL, 82 | distribution_params="alpha, plates=(:dl_Y, ), :ifr", 83 | ) 84 | mu = BayesianNetworkNode( 85 | network=bn, 86 | name="mu", 87 | node_type=BNN.NODE_TYPE_STOCHASTIC, 88 | is_observable=False, 89 | distribution=DIST_GAUSSIAN, 90 | distribution_params=("numpy.zeros(2), [[1e-5,0], [0, 1e-5]], " 91 | "plates=(10, )"), 92 | ) 93 | Lambda = BayesianNetworkNode( 94 | network=bn, 95 | name="Lambda", 96 | node_type=BNN.NODE_TYPE_STOCHASTIC, 97 | is_observable=False, 98 | distribution=DIST_WISHART, 99 | distribution_params="2, [[1e-5,0], [0, 1e-5]], plates=(10, )", 100 | ) 101 | Y = BayesianNetworkNode( 102 | network=bn, 103 | name="Y", 104 | node_type=BNN.NODE_TYPE_STOCHASTIC, 105 | is_observable=True, 106 | distribution=DIST_MIXTURE, 107 | distribution_params=("Z, @bayespy.nodes.Gaussian(), " 108 | "mu, Lambda, :noplates"), 109 | ) 110 | alpha.save() 111 | Z.save() 112 | mu.save() 113 | Lambda.save() 114 | Y.save() 115 | # 116 | Y_col_avg_logged = BayesianNetworkNodeColumn( 117 | node=Y, 118 | ref_model=ContentType.objects.get( 119 | model="userinfo", app_label="examples"), 120 | ref_column="avg_time_logged" 121 | ) 122 | Y_col_avg_pages_a = BayesianNetworkNodeColumn( 123 | node=Y, 124 | ref_model=ContentType.objects.get( 125 | model="userinfo", app_label="examples"), 126 | ref_column="avg_time_pages_a" 127 | ) 128 | Y_col_avg_logged.save() 129 | Y_col_avg_pages_a.save() 130 | # 131 | alpha_to_Z = BayesianNetworkEdge( 132 | network=bn, 133 | description="alpha -> Z", 134 | parent=alpha, 135 | child=Z 136 | ) 137 | Z_to_Y = BayesianNetworkEdge( 138 | network=bn, 139 | description="Z -> Y", 140 | parent=Z, 141 | child=Y 142 | ) 143 | mu_to_Y = BayesianNetworkEdge( 144 | network=bn, 145 | description="mu -> Y", 146 | parent=mu, 147 | child=Y 148 | ) 149 | Lambda_to_Y = BayesianNetworkEdge( 150 | network=bn, 151 | description="Lambda -> Y", 152 | parent=Lambda, 153 | child=Y 154 | ) 155 | alpha_to_Z.save() 156 | Z_to_Y.save() 157 | mu_to_Y.save() 158 | Lambda_to_Y.save() 159 | # Generate the image 160 | generate_bn_image(bn) 161 | 162 | 163 | def delete_clustering_bn_example(apps, schema_editor): 164 | BayesianNetwork = apps.get_model("bayesian_networks", 165 | "BayesianNetwork") 166 | 167 | BayesianNetwork.objects.get(name="Clustering (Example)").delete() 168 | 169 | 170 | class Migration(migrations.Migration): 171 | 172 | dependencies = [ 173 | ('examples', '0005_add_avg_times_clusters'), 174 | ] 175 | 176 | operations = [ 177 | migrations.RunPython(create_clustering_bn_example, 178 | delete_clustering_bn_example), 179 | ] 180 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/0007_userinfo_cluster_1.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-10-27 12:38 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('examples', '0006_clustering_bn_example'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='userinfo', 17 | name='cluster_1', 18 | field=models.CharField(blank=True, max_length=1, null=True, verbose_name='Cluster 1'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/0008_add_visits_to_pages.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-10-30 02:18 3 | from __future__ import unicode_literals 4 | import numpy as np 5 | 6 | from django.db import migrations, models 7 | from django.conf import settings 8 | 9 | 10 | def populate_visits(apps, schema_editor): 11 | UserInfo = apps.get_model("examples", "UserInfo") 12 | # Use a fixed seed for generate content 13 | np.random.seed(123456) 14 | # Size of table 15 | table_size = getattr(settings, "DJANGO_AI_EXAMPLES_USERINFO_SIZE", 200) 16 | # Floor'ed Normal around a 100 for total page visits 17 | visits_pages = np.floor(np.random.normal(100, 5, size=(table_size,))) 18 | # and around 40% of them are in pages of type A 19 | visits_pages_a = np.floor(np.random.normal(40, 2, size=(table_size,))) 20 | 21 | uis = UserInfo.objects.all() 22 | # Update the objects in the Model 23 | for index, ui in enumerate(uis): 24 | ui.visits_pages = visits_pages[index] 25 | ui.visits_pages_a = visits_pages_a[index] 26 | ui.save(update_fields=['visits_pages', 'visits_pages_a']) 27 | 28 | 29 | def unpopulate_visits(apps, schema_editor): 30 | """ 31 | This is for making the migration reversible, it doesn't do anything 32 | because the fields will be removed after by the reverse of AddField. 33 | """ 34 | pass 35 | 36 | 37 | class Migration(migrations.Migration): 38 | 39 | dependencies = [ 40 | ('examples', '0007_userinfo_cluster_1'), 41 | ] 42 | 43 | operations = [ 44 | migrations.AddField( 45 | model_name='userinfo', 46 | name='visits_pages_a', 47 | field=models.IntegerField( 48 | default=1, verbose_name='Visits on Pages A'), 49 | preserve_default=False, 50 | ), 51 | migrations.AddField( 52 | model_name='userinfo', 53 | name='visits_pages', 54 | field=models.IntegerField( 55 | default=1, verbose_name='Visits on Pages (Total)'), 56 | preserve_default=False, 57 | ), 58 | migrations.RunPython(populate_visits, 59 | unpopulate_visits), 60 | ] 61 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/0009_rename_avg_time_logged.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-10-31 20:10 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | def adjust_data_forward(apps, schema_editor): 9 | BayesianNetworkNode = apps.get_model( 10 | "bayesian_networks", "BayesianNetworkNode") 11 | Y = BayesianNetworkNode.objects.get( 12 | name="Y", network__name="Clustering (Example)" 13 | ) 14 | Y_col_avg_logged = Y.data_columns.get(ref_column="avg_time_logged") 15 | Y_col_avg_logged.ref_column = "avg_time_pages" 16 | Y_col_avg_logged.save() 17 | 18 | 19 | def adjust_data_backward(apps, schema_editor): 20 | BayesianNetworkNode = apps.get_model( 21 | "bayesian_networks", "BayesianNetworkNode") 22 | Y = BayesianNetworkNode.objects.get( 23 | name="Y", network__name="Clustering (Example)" 24 | ) 25 | Y_col_avg_time_pages = Y.data_columns.get(ref_column="avg_time_pages") 26 | Y_col_avg_time_pages.ref_column = "avg_time_logged" 27 | Y_col_avg_time_pages.save() 28 | 29 | 30 | class Migration(migrations.Migration): 31 | 32 | dependencies = [ 33 | ('examples', '0008_add_visits_to_pages'), 34 | ] 35 | 36 | operations = [ 37 | migrations.RenameField( 38 | model_name='userinfo', 39 | old_name='avg_time_logged', 40 | new_name='avg_time_pages', 41 | ), 42 | migrations.RunPython(adjust_data_forward, 43 | adjust_data_backward) 44 | ] 45 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/0010_tweaks_on_userinfo.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-11-07 04:41 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('examples', '0009_rename_avg_time_logged'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='userinfo', 17 | name='avg_time_pages', 18 | field=models.FloatField(blank=True, default=0, null=True, verbose_name='Average Time on Pages'), 19 | ), 20 | migrations.AlterField( 21 | model_name='userinfo', 22 | name='avg_time_pages_a', 23 | field=models.FloatField(blank=True, default=0, null=True, verbose_name='Average Time on Pages of type A'), 24 | ), 25 | migrations.AlterField( 26 | model_name='userinfo', 27 | name='sex', 28 | field=models.SmallIntegerField(blank=True, choices=[(0, 'M'), (1, 'F')], null=True, verbose_name='Sex'), 29 | ), 30 | migrations.AlterField( 31 | model_name='userinfo', 32 | name='visits_pages', 33 | field=models.IntegerField(default=0, verbose_name='Visits on Pages (Total)'), 34 | ), 35 | migrations.AlterField( 36 | model_name='userinfo', 37 | name='visits_pages_a', 38 | field=models.IntegerField(default=0, verbose_name='Visits on Pages A'), 39 | ), 40 | ] 41 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/0011_add_rest_avg_times_visits.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-11-09 08:34 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('examples', '0010_tweaks_on_userinfo'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='userinfo', 17 | name='avg_time_pages_b', 18 | field=models.FloatField( 19 | blank=True, default=0, null=True, 20 | verbose_name='Average Time on Pages of type B'), 21 | ), 22 | migrations.AddField( 23 | model_name='userinfo', 24 | name='avg_time_pages_c', 25 | field=models.FloatField( 26 | blank=True, default=0, null=True, 27 | verbose_name='Average Time on Pages of type C'), 28 | ), 29 | migrations.AddField( 30 | model_name='userinfo', 31 | name='avg_time_pages_d', 32 | field=models.FloatField( 33 | blank=True, default=0, null=True, 34 | verbose_name='Average Time on Pages of type D'), 35 | ), 36 | migrations.AddField( 37 | model_name='userinfo', 38 | name='avg_time_pages_e', 39 | field=models.FloatField( 40 | blank=True, default=0, null=True, 41 | verbose_name='Average Time on Pages of type E'), 42 | ), 43 | migrations.AddField( 44 | model_name='userinfo', 45 | name='avg_time_pages_f', 46 | field=models.FloatField( 47 | blank=True, default=0, null=True, 48 | verbose_name='Average Time on Pages of type F'), 49 | ), 50 | migrations.AddField( 51 | model_name='userinfo', 52 | name='avg_time_pages_g', 53 | field=models.FloatField( 54 | blank=True, default=0, null=True, 55 | verbose_name='Average Time on Pages of type G'), 56 | ), 57 | migrations.AddField( 58 | model_name='userinfo', 59 | name='avg_time_pages_h', 60 | field=models.FloatField( 61 | blank=True, default=0, null=True, 62 | verbose_name='Average Time on Pages of type H'), 63 | ), 64 | migrations.AddField( 65 | model_name='userinfo', 66 | name='avg_time_pages_i', 67 | field=models.FloatField( 68 | blank=True, default=0, null=True, 69 | verbose_name='Average Time on Pages of type I'), 70 | ), 71 | migrations.AddField( 72 | model_name='userinfo', 73 | name='avg_time_pages_j', 74 | field=models.FloatField( 75 | blank=True, default=0, null=True, 76 | verbose_name='Average Time on Pages of type J'), 77 | ), 78 | migrations.AddField( 79 | model_name='userinfo', 80 | name='visits_pages_b', 81 | field=models.IntegerField( 82 | default=0, verbose_name='Visits on Pages of type B'), 83 | ), 84 | migrations.AddField( 85 | model_name='userinfo', 86 | name='visits_pages_c', 87 | field=models.IntegerField( 88 | default=0, verbose_name='Visits on Pages of type C'), 89 | ), 90 | migrations.AddField( 91 | model_name='userinfo', 92 | name='visits_pages_d', 93 | field=models.IntegerField( 94 | default=0, verbose_name='Visits on Pages of type D'), 95 | ), 96 | migrations.AddField( 97 | model_name='userinfo', 98 | name='visits_pages_e', 99 | field=models.IntegerField( 100 | default=0, verbose_name='Visits on Pages of type E'), 101 | ), 102 | migrations.AddField( 103 | model_name='userinfo', 104 | name='visits_pages_f', 105 | field=models.IntegerField( 106 | default=0, verbose_name='Visits on Pages of type F'), 107 | ), 108 | migrations.AddField( 109 | model_name='userinfo', 110 | name='visits_pages_g', 111 | field=models.IntegerField( 112 | default=0, verbose_name='Visits on Pages of type G'), 113 | ), 114 | migrations.AddField( 115 | model_name='userinfo', 116 | name='visits_pages_h', 117 | field=models.IntegerField( 118 | default=0, verbose_name='Visits on Pages of type H'), 119 | ), 120 | migrations.AddField( 121 | model_name='userinfo', 122 | name='visits_pages_i', 123 | field=models.IntegerField( 124 | default=0, verbose_name='Visits on Pages of type I'), 125 | ), 126 | migrations.AddField( 127 | model_name='userinfo', 128 | name='visits_pages_j', 129 | field=models.IntegerField( 130 | default=0, verbose_name='Visits on Pages of type J'), 131 | ), 132 | migrations.AlterField( 133 | model_name='userinfo', 134 | name='visits_pages_a', 135 | field=models.IntegerField( 136 | default=0, verbose_name='Visits on Pages of type A'), 137 | ), 138 | ] 139 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/0012_update_clustering_bn.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-11-10 12:14 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | def update_bn_forward(apps, schema_editor): 9 | """ 10 | Updates the Clustering Example Bayesian Network Object. 11 | """ 12 | BayesianNetwork = apps.get_model("bayesian_networks", 13 | "BayesianNetwork") 14 | bn = BayesianNetwork.objects.get(name="Clustering (Example)") 15 | bn.results_storage = "dmf:examples.UserInfo.cluster_1" 16 | bn.engine_meta_iterations = 15 17 | bn.counter_threshold = 10 18 | bn.threshold_actions = ":recalculate" 19 | bn.save() 20 | 21 | 22 | def update_bn_backwards(apps, schema_editor): 23 | BayesianNetwork = apps.get_model("bayesian_networks", 24 | "BayesianNetwork") 25 | bn = BayesianNetwork.objects.get(name="Clustering (Example)") 26 | bn.results_storage = None, 27 | bn.engine_meta_iterations = 1 28 | bn.counter_threshold = None 29 | bn.threshold_actions = None 30 | bn.save() 31 | 32 | 33 | class Migration(migrations.Migration): 34 | 35 | dependencies = [ 36 | ('examples', '0011_add_rest_avg_times_visits'), 37 | ] 38 | 39 | operations = [ 40 | migrations.RunPython( 41 | update_bn_forward, update_bn_backwards 42 | ) 43 | ] 44 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/0013_commentofmysite.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-11-25 07:27 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('examples', '0012_update_clustering_bn'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='CommentOfMySite', 17 | fields=[ 18 | ('id', models.AutoField( 19 | auto_created=True, 20 | primary_key=True, serialize=False, 21 | verbose_name='ID') 22 | ), 23 | ('is_spam', models.BooleanField( 24 | default=False, 25 | help_text='If the object is Spam', 26 | verbose_name='Is Spam?') 27 | ), 28 | ('is_misclassified', models.BooleanField( 29 | default=False, 30 | help_text=('If the object has been misclassified by ' 31 | 'the Spam Filter'), 32 | verbose_name='Is Misclassified?') 33 | ), 34 | ('is_revised', models.BooleanField( 35 | default=False, 36 | help_text=('If the object classification has been revised ' 37 | 'by a Human'), 38 | verbose_name='Is Revised?') 39 | ), 40 | ('comment', models.TextField( 41 | verbose_name='Comment') 42 | ), 43 | ('user_id', models.SmallIntegerField( 44 | verbose_name='User ID') 45 | ), 46 | ], 47 | options={ 48 | 'verbose_name': 'Comment of my Site', 49 | 'verbose_name_plural': 'Comments of my Site', 50 | }, 51 | ), 52 | ] 53 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/0014_missing_ui_meta.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-11-27 08:16 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('examples', '0013_commentofmysite'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterModelOptions( 16 | name='userinfo', 17 | options={'verbose_name': 'User Info', 'verbose_name_plural': 'Users Infos'}, 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/0015_sfptenron_sfptyoutube.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-12-13 08:02 3 | from __future__ import unicode_literals 4 | import os 5 | import tarfile 6 | import urllib.request 7 | import random 8 | import zipfile 9 | import csv 10 | import io 11 | 12 | from django.db import migrations, models 13 | 14 | 15 | CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) 16 | ENRON_MAILS_FILE_NAME = os.path.join(CURRENT_DIR, "enron1.tar.gz") 17 | ENRON_MAILS_FILE_URL = ("http://www.aueb.gr/users/ion/data/enron-spam/" 18 | "preprocessed/enron1.tar.gz") 19 | YOUTUBE_COMMENTS_FILE_NAME = os.path.join(CURRENT_DIR, 20 | "YouTube-Spam-Collection-v1.zip") 21 | YOUTUBE_COMMENTS_FILE_URL = ("https://archive.ics.uci.edu/ml/" 22 | "machine-learning-databases/00380/" 23 | "YouTube-Spam-Collection-v1.zip") 24 | 25 | 26 | def confirm(question): 27 | """ 28 | https://gist.github.com/garrettdreyfus/8153571 29 | """ 30 | reply = str(input(' -> ' + question + ' (Y/n): ')).lower().strip() 31 | if reply == 'y' or reply == '': 32 | return True 33 | elif reply == 'n': 34 | return False 35 | else: 36 | return confirm("Mmmm... Please enter") 37 | 38 | 39 | def download_and_process_pretrain_data_files(apps, schema_editor): 40 | """ 41 | Forward Operation: Downloads if neccesary the sample data and populates 42 | Pre-Train Models. 43 | """ 44 | SFPTEnron = apps.get_model("examples", "SFPTEnron") 45 | SFPTYoutube = apps.get_model("examples", "SFPTYoutube") 46 | random.seed(1234567) 47 | 48 | # -> Download datasets if not exist 49 | if (not os.path.exists(ENRON_MAILS_FILE_NAME) or 50 | not os.path.exists(YOUTUBE_COMMENTS_FILE_NAME)): 51 | if confirm("Proceed to download pre-training datasets?"): 52 | if not os.path.exists(ENRON_MAILS_FILE_NAME): 53 | print(" Downloading Enron mails dataset...") 54 | urllib.request.urlretrieve( 55 | ENRON_MAILS_FILE_URL, 56 | ENRON_MAILS_FILE_NAME 57 | ) 58 | if not os.path.exists(YOUTUBE_COMMENTS_FILE_NAME): 59 | print(" Downloading Youtube comments dataset...") 60 | urllib.request.urlretrieve( 61 | YOUTUBE_COMMENTS_FILE_URL, 62 | YOUTUBE_COMMENTS_FILE_NAME 63 | ) 64 | # -> Process the Enron mails file 65 | with tarfile.open(name=ENRON_MAILS_FILE_NAME, mode="r:gz") as tfile: 66 | for member in tfile.getmembers(): 67 | if member.isfile() and "Summary" not in member.name: 68 | message = tfile.extractfile(member).read() 69 | SFPTEnron.objects.create( 70 | content=message.decode('raw_unicode_escape'), 71 | is_spam=("spam" in member.name), 72 | ) 73 | # -> Process Youtube comments file 74 | with zipfile.ZipFile(YOUTUBE_COMMENTS_FILE_NAME) as zfile: 75 | yt_files = [file for file in zfile.filelist 76 | if "MACOSX" not in file.filename] 77 | for yt_file in yt_files: 78 | with zfile.open(yt_file.filename) as csvfile: 79 | csvfile_sio = io.StringIO( 80 | csvfile.read().decode('raw_unicode_escape') 81 | ) 82 | reader = csv.DictReader(csvfile_sio) 83 | for row in reader: 84 | SFPTYoutube.objects.create( 85 | content=row['CONTENT'], 86 | is_spam=(row['CLASS'] == '1') 87 | ) 88 | 89 | 90 | def confirm_deletion_pretrain_data_files(apps, schema_editor): 91 | """ 92 | Backward Operation: Remove the file if deemed necessary, no need to remove 93 | the objects as the table will be removed. 94 | """ 95 | if not confirm( 96 | "Leave downloaded pre-train datasets files for the future?"): 97 | os.remove(ENRON_MAILS_FILE_NAME) 98 | os.remove(YOUTUBE_COMMENTS_FILE_NAME) 99 | 100 | 101 | class Migration(migrations.Migration): 102 | 103 | dependencies = [ 104 | ('examples', '0014_missing_ui_meta'), 105 | ] 106 | 107 | operations = [ 108 | migrations.CreateModel( 109 | name='SFPTEnron', 110 | fields=[ 111 | ('id', models.AutoField( 112 | auto_created=True, primary_key=True, serialize=False, 113 | verbose_name='ID')), 114 | ('content', models.TextField(verbose_name='Content')), 115 | ('is_spam', models.BooleanField( 116 | default=False, verbose_name='Is Spam?')), 117 | ], 118 | options={ 119 | 'verbose_name': 'Spam Filter Pre-training: Enron Email Data', 120 | 'verbose_name_plural': ('Spam Filter Pre-trainings: ' 121 | 'Enron Emails Data'), 122 | }, 123 | ), 124 | migrations.CreateModel( 125 | name='SFPTYoutube', 126 | fields=[ 127 | ('id', models.AutoField( 128 | auto_created=True, primary_key=True, serialize=False, 129 | verbose_name='ID')), 130 | ('content', models.TextField(verbose_name='Content')), 131 | ('is_spam', models.BooleanField( 132 | default=False, verbose_name='Is Spam?')), 133 | ], 134 | options={ 135 | 'verbose_name': ('Spam Filter Pre-training: ' 136 | 'Youtube Comment Data'), 137 | 'verbose_name_plural': ('Spam Filter Pre-trainings: ' 138 | 'Youtube Comments Data'), 139 | }, 140 | ), 141 | migrations.RunPython( 142 | download_and_process_pretrain_data_files, 143 | confirm_deletion_pretrain_data_files 144 | ), 145 | 146 | ] 147 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/0016_pretraining_minor_meta_update.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-12-18 07:12 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('examples', '0015_sfptenron_sfptyoutube'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterModelOptions( 16 | name='sfptenron', 17 | options={ 18 | 'verbose_name': 'Spam Filter Pre-Training: Enron Email Data', 19 | 'verbose_name_plural': ('Spam Filter Pre-Training: ' 20 | 'Enron Emails Data')}, 21 | ), 22 | migrations.AlterModelOptions( 23 | name='sfptyoutube', 24 | options={ 25 | 'verbose_name': 26 | 'Spam Filter Pre-Training: Youtube Comment Data', 27 | 'verbose_name_plural': ('Spam Filter Pre-Training: ' 28 | 'Youtube Comments Data')}, 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/0017_nullboolean_for_coms_is_spam.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-12-29 15:20 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('examples', '0016_pretraining_minor_meta_update'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='commentofmysite', 17 | name='is_spam', 18 | field=models.NullBooleanField( 19 | help_text='If the object is Spam', verbose_name='Is Spam?'), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/0018_spam_filter_example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2018-01-12 15:26 3 | from __future__ import unicode_literals 4 | 5 | from django.contrib.contenttypes.management import create_contenttypes 6 | from django.db import migrations 7 | 8 | 9 | def create_spam_filter_example(apps, schema_editor): 10 | # Hackery for ensuring all the apps are available 11 | from django.apps.registry import apps as dapps 12 | apps = dapps 13 | sf_config = apps.get_app_config('spam_filtering') 14 | sf_config.models_module = sf_config.models_module or True 15 | create_contenttypes(sf_config) 16 | sl_config = apps.get_app_config('supervised_learning') 17 | sl_config.models_module = sl_config.models_module or True 18 | create_contenttypes(sl_config) 19 | # 20 | SpamFilter = apps.get_model( 21 | "spam_filtering", "SpamFilter") 22 | SVC = apps.get_model( 23 | "supervised_learning", "SVC") 24 | DataColumn = apps.get_model( 25 | "base", "DataColumn") 26 | ContentType = apps.get_model( 27 | "contenttypes", "ContentType") 28 | 29 | svm = SVC( 30 | name="SVM for Spam (example)", 31 | kernel="linear", 32 | penalty_parameter=0.1, 33 | ) 34 | svm.save() 35 | sf = SpamFilter( 36 | name="Spam Filter for Comments (example)", 37 | classifier="supervised_learning.SVC|SVM for Spam (example)", 38 | counter_threshold=5, 39 | threshold_actions=":recalculate", 40 | spam_model_is_enabled=True, 41 | spam_model_model="examples.CommentOfMySite", 42 | labels_column="examples.commentofmysite.is_spam", 43 | pretraining="examples.SFPTYoutube", 44 | cv_is_enabled=True, 45 | cv_folds=10, 46 | cv_metric="average_precision", 47 | bow_is_enabled=True, 48 | bow_analyzer="word", 49 | bow_ngram_range_min=1, 50 | bow_ngram_range_max=3, 51 | bow_max_df=0.9, 52 | bow_min_df=0.001 53 | ) 54 | sf.save() 55 | dc = DataColumn( 56 | content_type=ContentType.objects.get(model="spamfilter", 57 | app_label="spam_filtering"), 58 | object_id=sf.id, 59 | ref_model=ContentType.objects.get(model="commentofmysite", 60 | app_label="examples"), 61 | ref_column="comment", 62 | position=0 63 | ) 64 | dc.save() 65 | 66 | 67 | def delete_spam_filter_example(apps, schema_editor): 68 | # Hackery for ensuring all the apps are available 69 | from django.apps.registry import apps as dapps 70 | apps = dapps 71 | # 72 | SpamFilter = apps.get_model( 73 | "spam_filtering", "SpamFilter") 74 | SVC = apps.get_model( 75 | "supervised_learning", "SVC") 76 | 77 | SVC.objects.get(name="SVM for Spam (example)").delete() 78 | SpamFilter.objects.get(name="Spam Filter for Comments (example)").delete() 79 | # DataColumn should be deleted on models.CASCADE 80 | 81 | 82 | class Migration(migrations.Migration): 83 | 84 | dependencies = [ 85 | ('supervised_learning', '__latest__'), 86 | ('spam_filtering', '__latest__'), 87 | ('examples', '0017_nullboolean_for_coms_is_spam'), 88 | ] 89 | 90 | operations = [ 91 | migrations.RunPython( 92 | create_spam_filter_example, 93 | delete_spam_filter_example 94 | ) 95 | ] 96 | -------------------------------------------------------------------------------- /django_ai/examples/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/math-a3k/django-ai/c2abc027bb47eb4a518b8daa8c0e73b2175cc59f/django_ai/examples/migrations/__init__.py -------------------------------------------------------------------------------- /django_ai/examples/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | 5 | from django.db import models 6 | 7 | if 'DJANGO_TEST' in os.environ: 8 | from django_ai.systems.spam_filtering.models import ( 9 | IsSpammable, 10 | SpamFilterPreTraining, 11 | ) 12 | APP_LABEL = "django_ai.examples" 13 | else: # pragma: no cover 14 | from systems.spam_filtering.models import ( 15 | IsSpammable, 16 | SpamFilterPreTraining, 17 | ) 18 | APP_LABEL = "examples" 19 | 20 | 21 | class UserInfo(models.Model): 22 | """ 23 | Example User Information Model 24 | """ 25 | SEX_CHOICES = [(0, "M"), (1, "F")] 26 | 27 | age = models.IntegerField("Age") 28 | sex = models.SmallIntegerField("Sex", choices=SEX_CHOICES, 29 | blank=True, null=True) 30 | # -> Metrics 31 | avg1 = models.FloatField("Average 1", blank=True, null=True) 32 | avg_time_pages = models.FloatField("Average Time on Pages", 33 | default=0, blank=True, null=True) 34 | visits_pages = models.IntegerField("Visits on Pages (Total)", 35 | default=0) 36 | avg_time_pages_a = models.FloatField("Average Time on Pages of type A", 37 | default=0, blank=True, null=True) 38 | visits_pages_a = models.IntegerField("Visits on Pages of type A", 39 | default=0) 40 | avg_time_pages_b = models.FloatField("Average Time on Pages of type B", 41 | default=0, blank=True, null=True) 42 | visits_pages_b = models.IntegerField("Visits on Pages of type B", 43 | default=0) 44 | avg_time_pages_c = models.FloatField("Average Time on Pages of type C", 45 | default=0, blank=True, null=True) 46 | visits_pages_c = models.IntegerField("Visits on Pages of type C", 47 | default=0) 48 | avg_time_pages_d = models.FloatField("Average Time on Pages of type D", 49 | default=0, blank=True, null=True) 50 | visits_pages_d = models.IntegerField("Visits on Pages of type D", 51 | default=0) 52 | avg_time_pages_e = models.FloatField("Average Time on Pages of type E", 53 | default=0, blank=True, null=True) 54 | visits_pages_e = models.IntegerField("Visits on Pages of type E", 55 | default=0) 56 | avg_time_pages_f = models.FloatField("Average Time on Pages of type F", 57 | default=0, blank=True, null=True) 58 | visits_pages_f = models.IntegerField("Visits on Pages of type F", 59 | default=0) 60 | avg_time_pages_g = models.FloatField("Average Time on Pages of type G", 61 | default=0, blank=True, null=True) 62 | visits_pages_g = models.IntegerField("Visits on Pages of type G", 63 | default=0) 64 | avg_time_pages_h = models.FloatField("Average Time on Pages of type H", 65 | default=0, blank=True, null=True) 66 | visits_pages_h = models.IntegerField("Visits on Pages of type H", 67 | default=0) 68 | avg_time_pages_i = models.FloatField("Average Time on Pages of type I", 69 | default=0, blank=True, null=True) 70 | visits_pages_i = models.IntegerField("Visits on Pages of type I", 71 | default=0) 72 | avg_time_pages_j = models.FloatField("Average Time on Pages of type J", 73 | default=0, blank=True, null=True) 74 | visits_pages_j = models.IntegerField("Visits on Pages of type J", 75 | default=0) 76 | # -> Results 77 | cluster_1 = models.CharField("Cluster 1", max_length=1, 78 | blank=True, null=True) 79 | 80 | class Meta: 81 | verbose_name = "User Info" 82 | verbose_name_plural = "Users Infos" 83 | app_label = APP_LABEL 84 | 85 | def __str__(self): 86 | return("{} - S: {}, A:{} - Group: {}".format( 87 | self.id, self.get_sex_display(), self.age, self.cluster_1) 88 | ) 89 | 90 | 91 | class CommentOfMySite(IsSpammable): 92 | SPAM_FILTER = "Spam Filter for Comments (example)" 93 | SPAMMABLE_FIELD = "comment" 94 | 95 | comment = models.TextField("Comment") 96 | user_id = models.SmallIntegerField("User ID") 97 | 98 | class Meta: 99 | verbose_name = "Comment of my Site" 100 | verbose_name_plural = "Comments of my Site" 101 | app_label = APP_LABEL 102 | 103 | def __str__(self): 104 | return("[U: {}] {}...".format(self.user_id, self.comment[:20])) 105 | 106 | 107 | class SFPTEnron(SpamFilterPreTraining): 108 | 109 | class Meta: 110 | verbose_name = "Spam Filter Pre-Training: Enron Email Data" 111 | verbose_name_plural = "Spam Filter Pre-Training: Enron Emails Data" 112 | app_label = APP_LABEL 113 | 114 | 115 | class SFPTYoutube(SpamFilterPreTraining): 116 | 117 | class Meta: 118 | verbose_name = "Spam Filter Pre-Training: Youtube Comment Data" 119 | verbose_name_plural = ("Spam Filter Pre-Training: " 120 | "Youtube Comments Data") 121 | app_label = APP_LABEL 122 | -------------------------------------------------------------------------------- /django_ai/examples/templates/examples/comments_of_my_site.html: -------------------------------------------------------------------------------- 1 | {% extends "examples/base.html" %} 2 | {% load static %} 3 | 4 | {% block title %} 5 | SmartDjango - Comments of my Site (A Page of Type {{ page_type }}) 6 | {% endblock title %} 7 | 8 | {% block main_title %} 9 | Comments of my Site 10 | {% endblock main_title %} 11 | 12 | {% block main_content %} 13 |
    14 |
    15 |
    16 |
    17 |
      18 |
    • Latest Comments
    • 19 | {% for comment in latest_comments %} 20 |
    • 21 |
      22 | {% if comment.is_spam %} 23 | bug_report 24 | {% else %} 25 | comment 26 | {% endif %} 27 | [User n° {{ comment.user_id }}] {{ comment.comment|truncatechars:50 }}
      28 |
      {{ comment.comment }}
      29 |
    • 30 | {% endfor %} 31 |
    32 |
    33 |
    34 |
    Post a Comment
    35 |
    36 | {% csrf_token %} 37 | {{ form.as_p }} 38 | 41 |
    42 |
    43 |
    44 |
    45 |
    46 | {% endblock main_content %} 47 | 48 | {% block ai_info %} 49 |

    You are using the spam filter {{ spam_filter.name }} with the {{ spam_filter.classifier }} classifier{% if spam_filter.pretraining %}, pre-trained with {{ spam_filter.pretraining }}{% endif %}.

    50 | {% endblock ai_info %} 51 | 52 | {% block ai_not_initialized_message %} 53 | There is no inference performed on the Bayesian Network for the metrics and / or the Spam Filter for Comments
    54 | You can run it from the corresponding admin's objects' change form. 55 | {% endblock ai_not_initialized_message %} -------------------------------------------------------------------------------- /django_ai/examples/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /django_ai/examples/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.conf.urls import url 4 | 5 | from . import views 6 | 7 | urlpatterns = [ 8 | url(r'^pages$', 9 | views.a_page_of_type_X, 10 | name="page"), 11 | url(r'^pages/(?P[A-J])/$', 12 | views.a_page_of_type_X, 13 | name="page"), 14 | url(r'^pages/(?P[A-J])/(?P[0-9]+)$', 15 | views.a_page_of_type_X, 16 | name="page"), 17 | url(r'^new-user$', 18 | views.new_user, 19 | name="new-user"), 20 | url(r'^metrics$', 21 | views.process_metrics, 22 | name="process-metrics"), 23 | url(r'^comments$', 24 | views.CommentsOfMySiteView.as_view(), 25 | name="comments-of-my-site"), 26 | url(r'^comments/(?P[0-9]+)$', 27 | views.CommentsOfMySiteView.as_view(), 28 | name="comments-of-my-site"), 29 | ] 30 | -------------------------------------------------------------------------------- /django_ai/examples/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | import random 5 | import math 6 | import json 7 | 8 | from django.shortcuts import (render, redirect, ) 9 | from django.http import HttpResponse 10 | from django.views.decorators.csrf import csrf_exempt 11 | from django.conf import settings 12 | from django.db.models import Avg 13 | from django.views.generic import CreateView 14 | from django.urls import reverse_lazy 15 | 16 | from .models import (UserInfo, CommentOfMySite, ) 17 | from .forms import (CommentOfMySiteForm, ) 18 | 19 | if 'DJANGO_TEST' in os.environ: 20 | from django_ai.bayesian_networks.models import BayesianNetwork 21 | from django_ai.systems.spam_filtering.models import SpamFilter 22 | from django_ai.examples import metrics 23 | else: # pragma: no cover 24 | from bayesian_networks.models import BayesianNetwork 25 | from systems.spam_filtering.models import SpamFilter 26 | from examples import metrics 27 | 28 | 29 | PAGES_COLORS = { 30 | "A": "green", "B": "deep-orange", "C": "blue-grey", "D": "brown", 31 | "E": "lime", "F": "light-blue", "G": "teal", "H": "deep-purple", 32 | "I": "indigo", "J": "red" 33 | } 34 | 35 | USERS = getattr(settings, "DJANGO_AI_EXAMPLES_USERINFO_SIZE", 200) 36 | 37 | 38 | def another_page(besides): 39 | other_pages = list(set(PAGES_COLORS.keys()) - set(besides)) 40 | return(random.choice(other_pages)) 41 | 42 | 43 | def a_page_of_type_X(request, page_type="A", user_id=random.randint(1, USERS)): 44 | """ 45 | View that renders pages of type X. 46 | user_id is for mimicking the user logged in (request.user). 47 | """ 48 | bn = BayesianNetwork.objects.get(name="Clustering (Example)") 49 | if not bn.is_inferred: 50 | return( 51 | render( 52 | request, 53 | template_name="examples/sample_page.html", 54 | context={"is_inferred": False} 55 | ) 56 | ) 57 | current_page = page_type 58 | avg_time_pages_X = "avg_time_pages_" + current_page.lower() 59 | user_info = UserInfo.objects.get(id=user_id) 60 | users_ids = UserInfo.objects.all().values_list("id", flat=True)\ 61 | .order_by('-id')[:200] # Prevents the browser becoming unresponsive 62 | cluster_avg_time_pages = \ 63 | bn.metadata["clusters_means"][user_info.cluster_1][0] 64 | # Calculate the cluster mean for the avg time of this type of page. 65 | # It may differ in the case of type A with the cluster mean of bn.metadata 66 | # calculated at the moment of performing the inference 67 | cluster_avg_times = UserInfo.objects\ 68 | .filter(cluster_1=user_info.cluster_1)\ 69 | .aggregate(**{avg_time_pages_X: Avg(avg_time_pages_X)}) 70 | # 71 | return( 72 | render( 73 | request, 74 | template_name="examples/sample_page.html", 75 | context={ 76 | "is_inferred": True, 77 | "page_type": page_type, 78 | "page_color": PAGES_COLORS[page_type], 79 | "current_user": int(user_id), 80 | "users_ids": users_ids, 81 | "other_page": another_page(besides=current_page), 82 | "cluster": user_info.cluster_1, 83 | "user_avg_time_pages_of_this_type": 84 | getattr(user_info, avg_time_pages_X), 85 | "cluster_avg_time_pages_of_this_type": 86 | cluster_avg_times[avg_time_pages_X], 87 | "user_avg_time_pages": user_info.avg_time_pages, 88 | "cluster_avg_time_pages": cluster_avg_time_pages, 89 | } 90 | ) 91 | ) 92 | 93 | 94 | def new_user(request): 95 | """ 96 | Mimmics creating a user by creating an UserInfo object. 97 | """ 98 | bn = BayesianNetwork.objects.get(name="Clustering (Example)") 99 | new_user = UserInfo.objects.create( 100 | sex=random.choice([0, 1]), 101 | age=math.floor(random.gauss(30, 2)), 102 | avg1=random.gauss(10, 5), 103 | cluster_1=bn.assign_cluster([0, 0]) 104 | ) 105 | # -> Increment the BN internal counter by 5 106 | # You **SHOULD NOT** do this, as it will trigger eventually an inference 107 | # on the model inside of the user's "navigation" request cycle (if 108 | # BN.counter_threshold is set) 109 | bn.counter += 5 110 | bn.save() 111 | # -> Instead, update the counter "directly" in the database: 112 | # BayesianNetwork.objects.filter(name="Clustering (Example)")\ 113 | # .update(counter=F("counter") + 5) 114 | # and / or schedule a model recalculation. 115 | return(redirect('page', page_type="A", user_id=new_user.id)) 116 | 117 | 118 | METRICS_PIPELINE = { 119 | m: metrics.__dict__[m] 120 | for m in metrics.__dict__ if m.startswith("metric") 121 | } 122 | 123 | 124 | @csrf_exempt 125 | def process_metrics(request, verbose=True): 126 | """ 127 | Minimal implementation of a Metricps Pipeline. 128 | 129 | The CSRF exemption is because it is highly unlikely that an external site 130 | posts values to our localhost to mess with our AI :) 131 | """ 132 | if request.method == "POST": 133 | data = json.loads(request.body.decode()) 134 | if verbose: 135 | print(data, METRICS_PIPELINE) 136 | for metric_name, metric in METRICS_PIPELINE.items(): 137 | metric(data) 138 | # -> Increment the BN internal counter by 1 139 | # No problem doing this, as it is outside of the user's "navigation" 140 | # request cycle: 141 | bn = BayesianNetwork.objects.get(name="Clustering (Example)") 142 | bn.counter += 1 143 | bn.save() 144 | # You could also update BN internal counter "directly", 145 | # without .save(): 146 | # BayesianNetwork.objects.filter(name="Clustering (Example)")\ 147 | # .update(counter=F("counter") + 1) 148 | # and / or schedule a model recalculation. 149 | return(HttpResponse(status=204)) 150 | else: 151 | return(HttpResponse(status=400)) 152 | 153 | 154 | class MetricsMixin(object): 155 | 156 | def get_context_data(self, **kwargs): 157 | context = super().get_context_data(**kwargs) 158 | # Usage metrics 159 | bn = BayesianNetwork.objects.get(name="Clustering (Example)") 160 | context['bn'] = bn 161 | user_info = UserInfo.objects.get(id=self.get_user_id()) 162 | if bn.is_inferred: 163 | cluster_avg_time_pages = \ 164 | bn.metadata["clusters_means"][user_info.cluster_1][0] 165 | context['cluster_avg_time_pages'] = cluster_avg_time_pages 166 | return context 167 | 168 | 169 | class CommentsOfMySiteView(MetricsMixin, CreateView): 170 | template_name = "examples/comments_of_my_site.html" 171 | form_class = CommentOfMySiteForm 172 | user_id = None 173 | 174 | def get_user_id(self): 175 | self.user_id = int(self.kwargs.get('user_id', 176 | random.randint(1, USERS))) 177 | return(self.user_id) 178 | 179 | def get_success_url(self): 180 | return(reverse_lazy('comments-of-my-site', 181 | kwargs={'user_id': self.get_user_id()})) 182 | 183 | def form_valid(self, form): 184 | # Increment the internal counter of the Spam Filter and 185 | # eventually trigger a recalculation of the model 186 | # BEWARE: This is inside the User's navigation request 187 | # cycle and is for demostrating purposes. You should 188 | # schedule a model update outside the cycle in production. 189 | sf_name = form.Meta.model.SPAM_FILTER 190 | spam_filter = SpamFilter.objects.get(name=sf_name) 191 | spam_filter.counter += 1 192 | spam_filter.save() 193 | return(super(CommentsOfMySiteView, self).form_valid(form)) 194 | 195 | def get_initial(self): 196 | return({'user_id': self.get_user_id()}) 197 | 198 | def get_context_data(self, **kwargs): 199 | context = super().get_context_data(**kwargs) 200 | spam_filter = SpamFilter.objects.get( 201 | name=CommentOfMySite.SPAM_FILTER) 202 | context['spam_filter'] = spam_filter 203 | context['is_inferred'] = (spam_filter.is_inferred and 204 | context['bn'].is_inferred) 205 | context['page_type'] = "C" 206 | context['page_color'] = PAGES_COLORS["C"] 207 | context['current_user'] = self.get_user_id() 208 | users_ids = UserInfo.objects.all().values_list("id", flat=True)\ 209 | .order_by('-id')[:200] # Prevents becoming unresponsive 210 | context['users_ids'] = users_ids 211 | context['users_dropdown_target'] = "comments-of-my-site" 212 | context['latest_comments'] = \ 213 | CommentOfMySite.objects.all().order_by("-id")[:10] 214 | return context 215 | -------------------------------------------------------------------------------- /django_ai/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", "django_ai.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 | -------------------------------------------------------------------------------- /django_ai/static/css/django_ai.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/math-a3k/django-ai/c2abc027bb47eb4a518b8daa8c0e73b2175cc59f/django_ai/static/css/django_ai.css -------------------------------------------------------------------------------- /django_ai/static/img/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/math-a3k/django-ai/c2abc027bb47eb4a518b8daa8c0e73b2175cc59f/django_ai/static/img/.gitignore -------------------------------------------------------------------------------- /django_ai/static/js/django_ai.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/math-a3k/django-ai/c2abc027bb47eb4a518b8daa8c0e73b2175cc59f/django_ai/static/js/django_ai.js -------------------------------------------------------------------------------- /django_ai/supervised_learning/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | default_app_config = "supervised_learning.apps.SupervisedLearningConfig" 4 | -------------------------------------------------------------------------------- /django_ai/supervised_learning/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.contrib import admin 4 | 5 | from .models.svm import (SVC, ) 6 | 7 | 8 | @admin.register(SVC) 9 | class SVCAdmin(admin.ModelAdmin): 10 | fieldsets = ( 11 | ("General", { 12 | 'fields': ('name', ) 13 | }), 14 | ("Miscellanous", { 15 | 'classes': ('collapse',), 16 | 'fields': ( 17 | ('engine_meta_iterations', 'engine_iterations'), 18 | ('counter', 'counter_threshold', 'threshold_actions'), 19 | ('engine_object_timestamp', 'image'), 20 | 'metadata', 21 | ), 22 | }), 23 | ("Model Parameters", { 24 | 'fields': ( 25 | ('kernel', ), 26 | ('penalty_parameter', ), 27 | ('kernel_poly_degree', 'kernel_coefficient', 28 | 'kernel_independent_term', ), 29 | ('class_weight', ), 30 | ) 31 | }), 32 | ("Implementation Parameters", { 33 | 'fields': ( 34 | ('decision_function_shape', ), 35 | ('estimate_probability', 'use_shrinking', ), 36 | ('tolerance', 'cache_size', 'random_seed', 'verbose', ), 37 | ) 38 | }) 39 | ) 40 | 41 | def get_form(self, request, obj=None, **kwargs): # pragma: no cover 42 | # Save obj reference in the request for future processing in Inline 43 | request._obj_ = obj 44 | form = super(SVCAdmin, self).get_form(request, obj, **kwargs) 45 | form.base_fields["metadata"].widget.attrs["disabled"] = "disabled" 46 | return(form) 47 | -------------------------------------------------------------------------------- /django_ai/supervised_learning/apps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | 5 | from django.apps import AppConfig 6 | 7 | 8 | class SupervisedLearningConfig(AppConfig): 9 | name = 'supervised_learning' 10 | verbose_name = '[django_ai] Supervised Learning' 11 | 12 | 13 | if 'DJANGO_TEST' in os.environ: 14 | SupervisedLearningConfig.name = 'django_ai.supervised_learning' 15 | -------------------------------------------------------------------------------- /django_ai/supervised_learning/models/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | 5 | if 'DJANGO_TEST' in os.environ: 6 | from django_ai.supervised_learning.models.svm import SVC 7 | else: 8 | from supervised_learning.models.svm import SVC 9 | -------------------------------------------------------------------------------- /django_ai/supervised_learning/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /django_ai/supervised_learning/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /django_ai/systems/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/math-a3k/django-ai/c2abc027bb47eb4a518b8daa8c0e73b2175cc59f/django_ai/systems/__init__.py -------------------------------------------------------------------------------- /django_ai/systems/spam_filtering/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | default_app_config = 'systems.spam_filtering.apps.SpamFilteringConfig' 4 | -------------------------------------------------------------------------------- /django_ai/systems/spam_filtering/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | 4 | from django.contrib import admin 5 | 6 | from .models import (SpamFilter, ) 7 | 8 | if 'DJANGO_TEST' in os.environ: 9 | from django_ai.base.admin import DataColumnInline 10 | else: # pragma: no cover 11 | from base.admin import DataColumnInline 12 | 13 | 14 | @admin.register(SpamFilter) 15 | class SpamFilterAdmin(admin.ModelAdmin): 16 | fieldsets = ( 17 | (None, { 18 | 'fields': ('name', ) 19 | }), 20 | ("Miscellanous", { 21 | 'classes': ('collapse',), 22 | 'fields': ( 23 | ('engine_meta_iterations', 'engine_iterations'), 24 | ('counter', 'counter_threshold', 'threshold_actions'), 25 | ('engine_object_timestamp', ), 26 | 'metadata', 27 | ), 28 | }), 29 | ("Spammable Model", { 30 | 'fields': ( 31 | 'spam_model_is_enabled', 'spam_model_model', 32 | ), 33 | }), 34 | ("Labels", { 35 | 'fields': ( 36 | 'labels_column', 37 | ), 38 | }), 39 | ("Classifier", { 40 | 'fields': ( 41 | 'classifier', 42 | ), 43 | }), 44 | ("Cross Validation", { 45 | 'fields': ( 46 | 'cv_is_enabled', 47 | ('cv_folds', 'cv_metric', ), 48 | ), 49 | }), 50 | ("Pre-Training", { 51 | 'fields': ( 52 | 'pretraining', 53 | ), 54 | }), 55 | ("Bag of Words Transformation", { 56 | 'fields': ( 57 | ('bow_is_enabled', ), 58 | ('bow_use_tf_idf', ), 59 | ('bow_analyzer', 'bow_ngram_range_min', 60 | 'bow_ngram_range_max', ), 61 | ('bow_max_df', 'bow_min_df', 'bow_max_features', ), 62 | ), 63 | }), 64 | ("Bag of Words Transformation - Miscellanous", { 65 | 'classes': ('collapse',), 66 | 'fields': ( 67 | ('bow_binary', ), 68 | ('bow_enconding', 'bow_decode_error', 'bow_strip_accents', ), 69 | ('bow_stop_words', 'bow_vocabulary', ), 70 | ), 71 | }), 72 | ) 73 | 74 | inlines = [DataColumnInline, ] 75 | 76 | fieldsets_and_inlines_order = ('f', 'f', 'f', 'i', ) 77 | 78 | def get_form(self, request, obj=None, **kwargs): # pragma: no cover 79 | # Save obj reference in the request for future processing in Inline 80 | request._obj_ = obj 81 | form = super(SpamFilterAdmin, self).get_form(request, obj, **kwargs) 82 | form.base_fields["metadata"].widget.attrs["disabled"] = "disabled" 83 | return(form) 84 | -------------------------------------------------------------------------------- /django_ai/systems/spam_filtering/apps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | 4 | from django.apps import AppConfig 5 | 6 | 7 | class SpamFilteringConfig(AppConfig): 8 | name = 'systems.spam_filtering' 9 | verbose_name = '[django-ai] Systems' 10 | 11 | 12 | if 'DJANGO_TEST' in os.environ: 13 | SpamFilteringConfig.name = 'django_ai.systems.spam_filtering' 14 | -------------------------------------------------------------------------------- /django_ai/systems/spam_filtering/static/js/admin/spam_filter.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | // Spammable Model UI 3 | sp_is_enabled_cb = $("input[name$='spam_model_is_enabled']"); 4 | sp_is_enabled_cb.change(function() { 5 | ie_cb = $(this); 6 | ie_next_fields = ie_cb.parent().parent().nextAll() 7 | data_columns_fieldset = ie_cb.closest("fieldset").next(); 8 | labels_fieldset = data_columns_fieldset.next(); 9 | 10 | if (ie_cb.is(':checked')) { 11 | ie_next_fields.show() 12 | data_columns_fieldset.hide(); 13 | labels_fieldset.hide(); 14 | } else { 15 | ie_next_fields.hide() 16 | data_columns_fieldset.show(); 17 | labels_fieldset.show(); 18 | } 19 | }); 20 | sp_is_enabled_cb.change(); 21 | // BoW UI 22 | bow_is_enabled_cb = $("input[name$='bow_is_enabled']"); 23 | bow_is_enabled_cb.change(function() { 24 | ie_cb = $(this); 25 | ie_next_fields = ie_cb.parent().parent().nextAll() 26 | bow_misc_fieldset = ie_cb.closest("fieldset").next(); 27 | 28 | if (ie_cb.is(':checked')) { 29 | ie_next_fields.show() 30 | bow_misc_fieldset.show(); 31 | } else { 32 | ie_next_fields.hide() 33 | bow_misc_fieldset.hide(); 34 | } 35 | }); 36 | bow_is_enabled_cb.change(); 37 | 38 | // CV UI 39 | cv_is_enabled_cb = $("input[name$='cv_is_enabled']"); 40 | cv_is_enabled_cb.change(function() { 41 | ie_cb = $(this); 42 | ie_next_fields = ie_cb.parent().parent().nextAll() 43 | 44 | if (ie_cb.is(':checked')) { 45 | ie_next_fields.show() 46 | } else { 47 | ie_next_fields.hide() 48 | } 49 | }); 50 | cv_is_enabled_cb.change(); 51 | 52 | $(document).on('formset:added', function(event, $row, formsetName) { 53 | // 54 | }); 55 | 56 | $(document).on('formset:removed', function(event, $row, formsetName) { 57 | // Row removed, nothing to do here yet 58 | }); 59 | 60 | })(django.jQuery); 61 | -------------------------------------------------------------------------------- /django_ai/systems/spam_filtering/templates/admin/spam_filtering/spamfilter/change_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base/admin/mixed_inlines_change_form.html" %} 2 | {% load admin_extras static i18n %} 3 | 4 | {% block after_related_objects %} 5 | {% if original.is_inferred %} 6 |

    {% trans "Inference" %}

    7 |

    {% trans "Current Inference" %}

    8 |

    9 | {% include "./snippets/model_info.html" with metadata=original.metadata.current_inference %} 10 |

    11 | {% if original.metadata.previous_inference %} 12 |

    {% trans "Previous Inference" %}

    13 |

    14 | {% include "./snippets/model_info.html" with metadata=original.metadata.previous_inference %} 15 |

    16 | {% endif %} 17 | 18 | {% endif %} 19 | {% ai_actions %} 20 | {% endblock %} 21 | 22 | {% block admin_change_form_document_ready %} 23 | {{ block.super }} 24 | 25 | {% endblock %} 26 | -------------------------------------------------------------------------------- /django_ai/systems/spam_filtering/templates/admin/spam_filtering/spamfilter/snippets/model_info.html: -------------------------------------------------------------------------------- 1 | {% load admin_extras %} 2 | 3 |
    4 |
    Vectorization
    5 |
    6 | {{ metadata.vectorizer_conf.str }} 7 |
    8 |
    Input Dimensionality
    9 |
    10 | {{ metadata.input_dimensionality.0 }} rows × {{ metadata.input_dimensionality.1 }} columns 11 |
    12 |
    Classifier
    13 |
    {{ metadata.classifier_conf.name }}: {{ metadata.classifier_conf.str }}
    14 |
    Cross Validation Estimation
    15 |
    {{ metadata.cv.conf.metric }} metric ({{ metadata.cv.conf.folds }} folds): {{ metadata.cv.mean|stringformat:".3f" }} ± {{ metadata.cv.2std|stringformat:".3f" }}
    16 |
    -------------------------------------------------------------------------------- /django_ai/systems/spam_filtering/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /django_ai/systems/spam_filtering/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /django_ai/templates/django_ai/base.html: -------------------------------------------------------------------------------- 1 | 2 | {% comment %} 3 | As the developer of this package, don't place anything here if you can help it 4 | since this allows developers to have interoperability between your template 5 | structure and their own. 6 | 7 | Example: Developer melding the 2SoD pattern to fit inside with another pattern:: 8 | 9 | {% extends "base.html" %} 10 | {% load static %} 11 | 12 | 13 | {% block extra_js %} 14 | 15 | 16 | {% block javascript %} 17 | 18 | {% endblock javascript %} 19 | 20 | {% endblock extra_js %} 21 | {% endcomment %} 22 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/complexity.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/complexity.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/complexity" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/complexity" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /docs/_static/django_ai.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/math-a3k/django-ai/c2abc027bb47eb4a518b8daa8c0e73b2175cc59f/docs/_static/django_ai.jpg -------------------------------------------------------------------------------- /docs/apps/base.rst: -------------------------------------------------------------------------------- 1 | .. _base: 2 | 3 | ================== 4 | ``django-ai`` Base 5 | ================== 6 | 7 | This app provides the basis for the framework. 8 | 9 | It provides the Abstract Base Models as well as common views and template-related code. 10 | 11 | Models 12 | ====== 13 | 14 | Abstract Base Models are documented in :ref:`api`. 15 | 16 | Views 17 | ===== 18 | 19 | .. autoclass:: base.views.RunActionView 20 | -------------------------------------------------------------------------------- /docs/apps/index.rst: -------------------------------------------------------------------------------- 1 | .. apps_index: 2 | 3 | Available Applications in `django-ai` 4 | ===================================== 5 | 6 | The following applications are available: 7 | 8 | .. toctree:: 9 | :maxdepth: 1 10 | 11 | base 12 | bayesian_networks 13 | supervised_learning 14 | spam_filtering 15 | examples 16 | -------------------------------------------------------------------------------- /docs/apps/supervised_learning.rst: -------------------------------------------------------------------------------- 1 | .. _supervised_learning: 2 | 3 | =================== 4 | Supervised Learning 5 | =================== 6 | 7 | This app provides `Supervised Learning `_ techniques for integrating them into systems or directly to your code. 8 | 9 | From an API point of view, each technique is a particular implementation of :ref:`api_supervised_learning`. 10 | 11 | .. _svm: 12 | 13 | Support Vector Machines (SVM) 14 | ============================= 15 | 16 | `Support Vector Machines `_ are provided by integrating the *scikit-learn* framework: http://scikit-learn.org. 17 | 18 | If you are not familiar with the framework, it is better at least take a glance on its `excellent documentation for the technique `_ for a better understanding on how the modelling is done. 19 | 20 | An example of integrating SVM into a system can be found in :ref:`example_spam_filtering`. 21 | 22 | SVM for Classification 23 | ---------------------- 24 | 25 | All the configuration can be done through the admin of Support Vector Machines for Classification - or more specifically, through the `change form`. 26 | 27 | The following fields are available for configuration: 28 | 29 | General 30 | ^^^^^^^ 31 | 32 | General fields (like ``Name``) and Miscellanous are documented in the :ref:`api_statistical_model`. 33 | 34 | This technique extends it with the following field: 35 | 36 | .. autoattribute:: supervised_learning.models.svm.SVC.image 37 | :annotation: Image 38 | 39 | The implementation uses *scikit-learn* as Engine, there is no need of setting more than 1 ``Engine Meta Iterations``. 40 | 41 | Model Parameters 42 | ^^^^^^^^^^^^^^^^ 43 | .. autoattribute:: supervised_learning.models.svm.SVC.kernel 44 | :annotation: SVM Kernel 45 | .. autoattribute:: supervised_learning.models.svm.SVC.penalty_parameter 46 | :annotation: Penalty parameter (C) of the error term. 47 | .. autoattribute:: supervised_learning.models.svm.SVC.kernel_poly_degree 48 | :annotation: Polynomial Kernel degree 49 | .. autoattribute:: supervised_learning.models.svm.SVC.kernel_coefficient 50 | :annotation: Kernel coefficient 51 | .. autoattribute:: supervised_learning.models.svm.SVC.kernel_independent_term 52 | :annotation: Kernel Independent Term 53 | .. autoattribute:: supervised_learning.models.svm.SVC.class_weight 54 | :annotation: Class Weight 55 | 56 | Implementation Parameters 57 | ^^^^^^^^^^^^^^^^^^^^^^^^^ 58 | .. autoattribute:: supervised_learning.models.svm.SVC.decision_function_shape 59 | :annotation: Decision Function Shape 60 | .. autoattribute:: supervised_learning.models.svm.SVC.estimate_probability 61 | :annotation: Estimate Probability? 62 | .. autoattribute:: supervised_learning.models.svm.SVC.use_shrinking 63 | :annotation: Use Shrinking Heuristic? 64 | .. autoattribute:: supervised_learning.models.svm.SVC.tolerance 65 | :annotation: Tolerance 66 | .. autoattribute:: supervised_learning.models.svm.SVC.cache_size 67 | :annotation: Kernel Cache Size (MB) 68 | .. autoattribute:: supervised_learning.models.svm.SVC.random_seed 69 | :annotation: 70 | .. autoattribute:: supervised_learning.models.svm.SVC.verbose 71 | :annotation: Be Verbose? 72 | -------------------------------------------------------------------------------- /docs/authors.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../AUTHORS.rst 2 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CONTRIBUTING.rst 2 | -------------------------------------------------------------------------------- /docs/history.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../HISTORY.rst 2 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. complexity documentation master file, created by 2 | sphinx-quickstart on Tue Jul 9 22:26:36 2013. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to django-ai's documentation! 7 | ================================================================= 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 1 13 | 14 | readme 15 | intro 16 | installation 17 | apps/index 18 | api/api 19 | contributing 20 | authors 21 | history 22 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | .. _installation: 2 | 3 | ============ 4 | Installation 5 | ============ 6 | 7 | For installing ``django-ai`` in your Django project use the following steps: 8 | 9 | 1. Activate your virtual environment and then:: 10 | 11 | pip install django-ai 12 | 13 | 2. Add it to your `INSTALLED_APPS`:: 14 | 15 | INSTALLED_APPS = ( 16 | ... 17 | # Dependencies 18 | 'nested_admin', 19 | 20 | # django-ai apps 21 | 'django_ai.base', 22 | 'django_ai.bayesian_networks', 23 | 'django_ai.supervised_learning', 24 | 'django_ai.systems.spam_filtering', 25 | 26 | # optional but highly recommended 27 | 'django_ai.examples', 28 | ... 29 | ) 30 | 31 | The ``django_ai.examples`` is optional but it is highly recommended that you keep it as a reference. 32 | 33 | 3. Create the migrations for the dependencies and apply them:: 34 | 35 | python manage.py makemigrations 36 | python manage.py migrate 37 | 38 | 4. Add django-ai's apps URL patterns and its dependencies:: 39 | 40 | urlpatterns = [ 41 | ... 42 | url(r'^nested_admin/', # Dependency 43 | include('nested_admin.urls')), 44 | url(r'^django-ai/', 45 | include(django_ai.base.urls)), 46 | ... 47 | ] 48 | 49 | 5. Ensure that the ``admin`` app is enabled. 50 | 51 | 6. Ensure that your ``static`` serving is properly configured, if not you may have to add to your ``urls.py``:: 52 | 53 | ... 54 | from django.conf.urls.static import static 55 | 56 | urlpatterns = [ 57 | ... 58 | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 59 | 60 | For reference, there is a working :download:`settings.py <../django_ai/django_ai/settings.py>` and :download:`urls.py <../django_ai/django_ai/urls.py>` in the source distribution for further troubleshooting if necessary. 61 | -------------------------------------------------------------------------------- /docs/intro.rst: -------------------------------------------------------------------------------- 1 | .. _introduction: 2 | 3 | ============ 4 | Introduction 5 | ============ 6 | 7 | ``django-ai`` is a collection of apps for integrating statistical models into your Django project, providing a framework so you can implement machine learning conveniently. 8 | 9 | It aims to integrate several libraries and engines supplying your Django apps with a set of tools for leveraging your project functionality with the data generated within. 10 | 11 | The integration is done through Django models - where most of the data is modelled and stored - and with an API focused on integrating seamlessly within Django projects’ best practices and patterns. This data is what will feed the statistical models and then those models will be used in the project's code to augment its utility and functionality. 12 | 13 | The rationale of ``django-ai`` is to provide for each statistical model - system or technique bundled - a front-end for configuration and an API for integrating it into your code. 14 | 15 | The front-end aims to let you choose which parts of the Django models will be used and "configure" its parameters: conveniently "state" the statistical model. Currently, it is admin-based. 16 | 17 | Once you are "happy" with your model you can incorporate it into your code, i.e. in a view: 18 | 19 | .. code-block:: python 20 | 21 | from django_ai.apps.bayesian_networks.models import BayesianNetwork 22 | 23 | def my_view(request): 24 | user_classifier = BayesianNetwork.objects.get(name='User BN') 25 | user_class = user_classifier.predict(request.user) 26 | # Do something with the user classification 27 | return(redirect('promotions', user_class)) 28 | 29 | This "kind of hybrid approach" is what gives you convenience: you can state the model easily in the front-end (admin), and after you incorporate it in your code, you can maintain, improve or update it in the front-end. 30 | 31 | By design, ``django-ai`` tries to take outside Django's users' request-response cycle as many calculations as possible. This means all the heavy stuff like model estimation is done once and stored. Inside the request cycle - i.e. in a view or a template - it is just regular queries to the database or operations that are "pretty straight-forward", like cluster assignment or classification. 32 | 33 | See :ref:`examples` for more. 34 | 35 | ``django-ai`` aims to provide with 2 classes of apps or statistical models: "low level" and "high level". 36 | 37 | "Low level" are those "basic" models or techniques, such as Bayesian Networks, Support Vector Machines, Classification and Aggregation Trees, Random Forests, Clustering algorithms, Neural Networks, etc. Those are the building blocks for the machine to learn and construct its intelligence. 38 | 39 | "High level" are systems, they are composed from "low level" ones and provide end-to-end functionality on a certain task, such as a recommender system or a spam filter. 40 | 41 | The ``django-ai`` apps will integrate the statistical models already implemented in other libraries as much as possible, using them as engines and becoming also a front-end to them for Django projects. 42 | 43 | The primary or main integration is with Python codebases - for obvious reasons. In the future, integration with other codebases is in sight, such as ``R`` where the amount of statistical models implemented is the biggest and greatest or ``Haskell``, where the purity of the language makes it ideal for expressing mathematical models. 44 | 45 | This is an Introduction to the Philosophy, Design, Architecture and Roadmap of ``django-ai``. 46 | 47 | You are welcome to join the community of users and developers. 48 | 49 | Last but not least: ``django-ai`` is, and will always be, Free Software (Free as in Freedom). If you can't patent Math, you can't patent Software. Did Newton hide something from you? :) Open Knowledge is better for all :) 50 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | goto end 41 | ) 42 | 43 | if "%1" == "clean" ( 44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 45 | del /q /s %BUILDDIR%\* 46 | goto end 47 | ) 48 | 49 | 50 | %SPHINXBUILD% 2> nul 51 | if errorlevel 9009 ( 52 | echo. 53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 54 | echo.installed, then set the SPHINXBUILD environment variable to point 55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 56 | echo.may add the Sphinx directory to PATH. 57 | echo. 58 | echo.If you don't have Sphinx installed, grab it from 59 | echo.http://sphinx-doc.org/ 60 | exit /b 1 61 | ) 62 | 63 | if "%1" == "html" ( 64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 68 | goto end 69 | ) 70 | 71 | if "%1" == "dirhtml" ( 72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 76 | goto end 77 | ) 78 | 79 | if "%1" == "singlehtml" ( 80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 84 | goto end 85 | ) 86 | 87 | if "%1" == "pickle" ( 88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can process the pickle files. 92 | goto end 93 | ) 94 | 95 | if "%1" == "json" ( 96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 97 | if errorlevel 1 exit /b 1 98 | echo. 99 | echo.Build finished; now you can process the JSON files. 100 | goto end 101 | ) 102 | 103 | if "%1" == "htmlhelp" ( 104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 105 | if errorlevel 1 exit /b 1 106 | echo. 107 | echo.Build finished; now you can run HTML Help Workshop with the ^ 108 | .hhp project file in %BUILDDIR%/htmlhelp. 109 | goto end 110 | ) 111 | 112 | if "%1" == "qthelp" ( 113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 117 | .qhcp project file in %BUILDDIR%/qthelp, like this: 118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\complexity.qhcp 119 | echo.To view the help file: 120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\complexity.ghc 121 | goto end 122 | ) 123 | 124 | if "%1" == "devhelp" ( 125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished. 129 | goto end 130 | ) 131 | 132 | if "%1" == "epub" ( 133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 137 | goto end 138 | ) 139 | 140 | if "%1" == "latex" ( 141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 145 | goto end 146 | ) 147 | 148 | if "%1" == "latexpdf" ( 149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 150 | cd %BUILDDIR%/latex 151 | make all-pdf 152 | cd %BUILDDIR%/.. 153 | echo. 154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 155 | goto end 156 | ) 157 | 158 | if "%1" == "latexpdfja" ( 159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 160 | cd %BUILDDIR%/latex 161 | make all-pdf-ja 162 | cd %BUILDDIR%/.. 163 | echo. 164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 165 | goto end 166 | ) 167 | 168 | if "%1" == "text" ( 169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 170 | if errorlevel 1 exit /b 1 171 | echo. 172 | echo.Build finished. The text files are in %BUILDDIR%/text. 173 | goto end 174 | ) 175 | 176 | if "%1" == "man" ( 177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 178 | if errorlevel 1 exit /b 1 179 | echo. 180 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 181 | goto end 182 | ) 183 | 184 | if "%1" == "texinfo" ( 185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 186 | if errorlevel 1 exit /b 1 187 | echo. 188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 189 | goto end 190 | ) 191 | 192 | if "%1" == "gettext" ( 193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 194 | if errorlevel 1 exit /b 1 195 | echo. 196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 197 | goto end 198 | ) 199 | 200 | if "%1" == "changes" ( 201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 202 | if errorlevel 1 exit /b 1 203 | echo. 204 | echo.The overview file is in %BUILDDIR%/changes. 205 | goto end 206 | ) 207 | 208 | if "%1" == "linkcheck" ( 209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 210 | if errorlevel 1 exit /b 1 211 | echo. 212 | echo.Link check complete; look for any errors in the above output ^ 213 | or in %BUILDDIR%/linkcheck/output.txt. 214 | goto end 215 | ) 216 | 217 | if "%1" == "doctest" ( 218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 219 | if errorlevel 1 exit /b 1 220 | echo. 221 | echo.Testing of doctests in the sources finished, look at the ^ 222 | results in %BUILDDIR%/doctest/output.txt. 223 | goto end 224 | ) 225 | 226 | if "%1" == "xml" ( 227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 228 | if errorlevel 1 exit /b 1 229 | echo. 230 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 231 | goto end 232 | ) 233 | 234 | if "%1" == "pseudoxml" ( 235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 236 | if errorlevel 1 exit /b 1 237 | echo. 238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 239 | goto end 240 | ) 241 | 242 | :end 243 | -------------------------------------------------------------------------------- /docs/readme.rst: -------------------------------------------------------------------------------- 1 | ========= 2 | django-ai 3 | ========= 4 | 5 | .. image:: https://badge.fury.io/py/django-ai.svg 6 | :target: https://badge.fury.io/py/django-ai 7 | 8 | .. image:: https://travis-ci.org/math-a3k/django-ai.svg?branch=master 9 | :target: https://travis-ci.org/math-a3k/django-ai 10 | 11 | .. image:: https://codecov.io/gh/math-a3k/django-ai/branch/master/graph/badge.svg 12 | :target: https://codecov.io/gh/math-a3k/django-ai 13 | 14 | Artificial Intelligence for Django 15 | ================================== 16 | 17 | ``django-ai`` is a collection of apps for integrating statistical models into your Django project, providing a framework so you can implement machine learning conveniently. 18 | 19 | It integrates several libraries and engines your Django app with a set of tools so you can leverage the data generated in your project. 20 | 21 | .. image:: ../django_ai.jpg 22 | :target: :ref:`introduction` 23 | 24 | Documentation 25 | ------------- 26 | 27 | The full documentation is at https://django-ai.readthedocs.io or the `/docs` directory for offline reading. 28 | 29 | Features 30 | -------- 31 | 32 | * :ref:`Bayesian Networks `: Integrate Bayesian Networks through your models using the `BayesPy framework `_. 33 | * :ref:`Spam Filtering `: Integrate Spam Filters to your Django project using the `scikit-learn framework `_. 34 | 35 | See the :ref:`Introduction ` section in the documentation for more information. 36 | 37 | Communication Channels 38 | ---------------------- 39 | 40 | * Mailing List: django-ai@googlegroups.com 41 | * Chat: https://gitter.im/django-ai/django-ai 42 | * GitHub: https://github.com/math-a3k/django-ai/issues 43 | * Stack-Overflow: https://stackoverflow.com/questions/tagged/django-ai 44 | * AI Stack Exchange: https://ai.stackexchange.com/questions/tagged/django-ai 45 | 46 | 47 | .. _quickstart: 48 | 49 | Quickstart 50 | ---------- 51 | 52 | The easiest way of trying `django-ai` is inside its package: 53 | 54 | 1. Create a virtual environment and activate it:: 55 | 56 | python3 -m venv django-ai_env 57 | source django-ai_env/bin/activate 58 | 59 | 2. Upgrade ``pip`` and install ``django-ai``:: 60 | 61 | (django-ai_env) pip install --upgrade pip 62 | (django-ai_env) pip install django-ai 63 | 64 | 3. Change into the `django-ai` directory, i.e.:: 65 | 66 | (django-ai_env) cd django-ai_env/lib/python3.5/site-packages/django_ai 67 | 68 | 4. Create the migrations for the dependencies and apply them:: 69 | 70 | python manage.py makemigrations 71 | python manage.py migrate 72 | 73 | 5. Create a superuser:: 74 | 75 | python manage.py createsuperuser 76 | 77 | 6. Start the development server and visit http://127.0.0.1:8000/admin/, look at the examples and start creating your statistical models:: 78 | 79 | python manage.py runserver 80 | 81 | You can also clone it from the repository and install the requirements in a virtualenv:: 82 | 83 | git clone git@github.com:math-a3k/django-ai.git 84 | 85 | and following the previous steps, install the requirements - ``pip install -r requirements.txt`` - in a virtual environment instead of the package. 86 | 87 | For installing it in your project, please refer :ref:`here `. 88 | 89 | 90 | Running Tests 91 | ------------- 92 | 93 | Does the code actually work? 94 | 95 | :: 96 | 97 | source /bin/activate 98 | (myenv) $ pip install -r requirements_test.txt 99 | (myenv) $ PYTHONHASHSEED=0 python runtests.py 100 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | from __future__ import unicode_literals, absolute_import 4 | 5 | import os 6 | import sys 7 | 8 | if __name__ == "__main__": 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings") 10 | from django.core.management import execute_from_command_line 11 | 12 | execute_from_command_line(sys.argv) 13 | -------------------------------------------------------------------------------- /misc/django_dag_0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11 on 2017-09-01 22:03 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Edge', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ], 21 | options={ 22 | 'abstract': False, 23 | }, 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | bayespy==0.5.6 3 | Django>=1.11, <2.1 4 | django-picklefield==1.0.0 5 | django-nested-admin==3.0.20 6 | graphviz==0.6 7 | jsonfield==2.0.2 8 | numpy<1.14 9 | Pillow==6.2.0 10 | pyparsing==2.2.0 11 | scikit-learn>=0.19.1 12 | -------------------------------------------------------------------------------- /requirements_dev.txt: -------------------------------------------------------------------------------- 1 | -r requirements_test.txt 2 | 3 | bumpversion==0.5.3 4 | wheel==0.29.0 5 | ipdb==0.10.3 6 | Sphinx==1.6.3 -------------------------------------------------------------------------------- /requirements_rtd.txt: -------------------------------------------------------------------------------- 1 | alabaster==0.7.10 2 | Babel==2.5.0 3 | bayespy==0.5.6 4 | certifi==2017.7.27.1 5 | chardet==3.0.4 6 | cycler==0.10.0 7 | decorator==4.1.2 8 | Django==1.11.23 9 | django-nested-admin==3.0.20 10 | django-picklefield==1.0.0 11 | docutils==0.14 12 | flake8==3.4.1 13 | graphviz==0.6 14 | h5py==2.7.1 15 | idna==2.6 16 | imagesize==0.7.1 17 | jedi==0.10.2 18 | Jinja2==2.9.6 19 | jsonfield==2.0.2 20 | MarkupSafe==1.0 21 | matplotlib==2.0.2 22 | mccabe==0.6.1 23 | numpy==1.13.1 24 | olefile==0.44 25 | pbr==3.1.1 26 | pexpect==4.2.1 27 | pickleshare==0.7.4 28 | Pillow==6.2.0 29 | pkginfo==1.4.1 30 | pluggy==0.5.2 31 | prompt-toolkit==1.0.15 32 | ptyprocess==0.5.2 33 | py==1.4.34 34 | pycodestyle==2.3.1 35 | pyflakes==1.5.0 36 | Pygments==2.2.0 37 | pyparsing==2.2.0 38 | python-dateutil==2.6.1 39 | python-monkey-business==1.0.0 40 | pytz==2017.2 41 | requests==2.20.0 42 | requests-toolbelt==0.8.0 43 | scikit-learn==0.19.1 44 | scipy==0.19.1 45 | simplegeneric==0.8.1 46 | six==1.10.0 47 | snowballstemmer==1.2.1 48 | Sphinx==1.6.3 49 | sphinxcontrib-websupport==1.0.1 50 | tqdm==4.15.0 51 | traitlets==4.3.2 52 | urllib3==1.24.2 53 | wcwidth==0.1.7 -------------------------------------------------------------------------------- /requirements_test.txt: -------------------------------------------------------------------------------- 1 | -r requirements.txt 2 | 3 | coverage==4.4.1 4 | mock>=1.0.1 5 | flake8>=2.1.0 6 | tox>=1.7.0 7 | codecov>=2.0.0 8 | -------------------------------------------------------------------------------- /runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 3 | from __future__ import unicode_literals, absolute_import 4 | 5 | import os 6 | import sys 7 | 8 | import django 9 | from django.conf import settings 10 | from django.test.utils import get_runner 11 | 12 | 13 | def run_tests(*test_args): 14 | if not test_args: 15 | test_args = ['tests'] 16 | 17 | os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.settings' 18 | os.environ['DJANGO_TEST'] = 'True' 19 | django.setup() 20 | TestRunner = get_runner(settings) 21 | test_runner = TestRunner() 22 | failures = test_runner.run_tests(test_args) 23 | sys.exit(bool(failures)) 24 | 25 | 26 | if __name__ == '__main__': 27 | run_tests(*sys.argv[1:]) 28 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 0.0.2.1 3 | commit = True 4 | tag = True 5 | 6 | [bumpversion:file:setup.py] 7 | 8 | [bumpversion:file:django_ai/__init__.py] 9 | 10 | [wheel] 11 | universal = 0 12 | 13 | [flake8] 14 | ignore = D203 15 | exclude = 16 | django_ai/migrations, 17 | .git, 18 | .tox, 19 | docs/conf.py, 20 | build, 21 | dist 22 | max-line-length = 119 23 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import os 4 | import re 5 | import sys 6 | 7 | try: 8 | from setuptools import setup 9 | except ImportError: 10 | from distutils.core import setup 11 | 12 | 13 | def get_version(*file_paths): 14 | """Retrieves the version from django_ai/__init__.py""" 15 | filename = os.path.join(os.path.dirname(__file__), *file_paths) 16 | version_file = open(filename).read() 17 | version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", 18 | version_file, re.M) 19 | if version_match: 20 | return version_match.group(1) 21 | raise RuntimeError('Unable to find version string.') 22 | 23 | 24 | version = get_version("django_ai", "__init__.py") 25 | 26 | 27 | if sys.argv[-1] == 'publish': 28 | try: 29 | import wheel 30 | print("Wheel version: ", wheel.__version__) 31 | except ImportError: 32 | print('Wheel library missing. Please run "pip install wheel"') 33 | sys.exit() 34 | os.system('python setup.py sdist upload') 35 | os.system('python setup.py bdist_wheel upload') 36 | sys.exit() 37 | 38 | if sys.argv[-1] == 'tag': 39 | print("Tagging the version on git:") 40 | os.system("git tag -a %s -m 'version %s'" % (version, version)) 41 | os.system("git push --tags") 42 | sys.exit() 43 | 44 | readme = open('README.rst').read() 45 | 46 | setup( 47 | name='django-ai', 48 | version=version, 49 | description="""Artificial Intelligence for Django""", 50 | long_description=readme, 51 | author='Rodrigo Gadea', 52 | author_email='matematica.a3k@gmail.com', 53 | url='https://github.com/math-a3k/django-ai', 54 | packages=[ 55 | 'django_ai', 56 | ], 57 | include_package_data=True, 58 | install_requires=[ 59 | 'numpy<1.14', 60 | 'bayespy==0.5.6', 61 | 'Django>=1.11, <2.1', 62 | 'django-nested-admin==3.0.20', 63 | 'django-picklefield==1.0.0', 64 | 'graphviz==0.6', 65 | 'jsonfield==2.0.2', 66 | 'Pillow==6.2.0', 67 | 'scikit-learn>=0.19.1', 68 | ], 69 | license="LGPLv3", 70 | zip_safe=False, 71 | keywords='django-ai', 72 | classifiers=[ 73 | 'Development Status :: 5 - Production/Stable', 74 | 'Framework :: Django', 75 | 'Framework :: Django :: 1.11', 76 | 'Intended Audience :: Developers', 77 | ('License :: OSI Approved :: ' 78 | 'GNU Lesser General Public License v3 or later (LGPLv3+)'), 79 | 'Natural Language :: English', 80 | 'Programming Language :: Python :: 3', 81 | 'Programming Language :: Python :: 3.5', 82 | 'Topic :: Scientific/Engineering :: Artificial Intelligence', 83 | 'Topic :: Scientific/Engineering :: Mathematics', 84 | 'Topic :: Software Development :: Libraries :: Application Frameworks', 85 | ], 86 | ) 87 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from .apps import * 4 | -------------------------------------------------------------------------------- /tests/apps/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from .base import * 4 | from .bayesian_networks import * 5 | from .supervised_learning import * 6 | -------------------------------------------------------------------------------- /tests/apps/base/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/math-a3k/django-ai/c2abc027bb47eb4a518b8daa8c0e73b2175cc59f/tests/apps/base/__init__.py -------------------------------------------------------------------------------- /tests/apps/bayesian_networks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/math-a3k/django-ai/c2abc027bb47eb4a518b8daa8c0e73b2175cc59f/tests/apps/bayesian_networks/__init__.py -------------------------------------------------------------------------------- /tests/apps/supervised_learning/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/math-a3k/django-ai/c2abc027bb47eb4a518b8daa8c0e73b2175cc59f/tests/apps/supervised_learning/__init__.py -------------------------------------------------------------------------------- /tests/apps/supervised_learning/test_svm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | test_svm.py 6 | ------------ 7 | 8 | Tests for `django-ai.supervised_learning`\ 's `svm` module. 9 | """ 10 | import random 11 | import numpy as np 12 | 13 | from django.test import TestCase 14 | from django_ai.supervised_learning.models import svm 15 | 16 | 17 | class TestSVM(TestCase): 18 | 19 | def setUp(self): 20 | # Set the seeds 21 | random.seed(123456) 22 | np.random.seed(123456) 23 | # -> SVM 1 24 | self.svm1, _ = svm.SVC.objects.get_or_create( 25 | name="svm1" 26 | ) 27 | 28 | def test_svm_engine_object(self): 29 | X = np.array([[-1, -1], [-2, -1], [1, 1], [2, 1]]) 30 | y = np.array([1, 1, 2, 2]) 31 | classifier = self.svm1.get_engine_object() 32 | classifier.fit(X, y) 33 | self.assertEqual(classifier.predict([[-0.8, -1]]), [1]) 34 | 35 | def test_conf_dict(self): 36 | self.setUp() 37 | self.svm1.kernel = 'poly' 38 | confdict = self.svm1.get_conf_dict() 39 | self.assertTrue("Degree" in confdict["str"]) 40 | self.assertTrue("gamma" in confdict["str"]) 41 | self.assertTrue("Indep." in confdict["str"]) 42 | -------------------------------------------------------------------------------- /tests/apps/systems/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/math-a3k/django-ai/c2abc027bb47eb4a518b8daa8c0e73b2175cc59f/tests/apps/systems/__init__.py -------------------------------------------------------------------------------- /tests/settings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 2 | from __future__ import unicode_literals, absolute_import 3 | 4 | import os 5 | import django 6 | 7 | 8 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 9 | 10 | DEBUG = True 11 | USE_TZ = True 12 | 13 | # SECURITY WARNING: keep the secret key used in production secret! 14 | SECRET_KEY = "**************************************************" 15 | 16 | DATABASES = { 17 | "default": { 18 | "ENGINE": "django.db.backends.sqlite3", 19 | "NAME": ":memory:", 20 | } 21 | } 22 | 23 | ROOT_URLCONF = "tests.urls" 24 | 25 | INSTALLED_APPS = [ 26 | 'django.contrib.admin', 27 | 'django.contrib.auth', 28 | 'django.contrib.contenttypes', 29 | 'django.contrib.sessions', 30 | 'django.contrib.messages', 31 | 'django.contrib.staticfiles', 32 | 33 | 'nested_admin', # Dependency 34 | 35 | 'django_ai.base.apps.BaseConfig', 36 | 'django_ai.bayesian_networks.apps.BayesianNetworksConfig', 37 | 'django_ai.supervised_learning.apps.SupervisedLearningConfig', 38 | 'django_ai.systems.spam_filtering.apps.SpamFilteringConfig', 39 | 'tests.test_models' 40 | ] 41 | 42 | TEMPLATES = [ 43 | { 44 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 45 | 'DIRS': [], 46 | 'APP_DIRS': True, 47 | 'OPTIONS': { 48 | 'context_processors': [ 49 | 'django.template.context_processors.debug', 50 | 'django.template.context_processors.request', 51 | 'django.contrib.auth.context_processors.auth', 52 | 'django.contrib.messages.context_processors.messages', 53 | ], 54 | }, 55 | }, 56 | ] 57 | 58 | MIDDLEWARE = [ 59 | 'django.contrib.sessions.middleware.SessionMiddleware', 60 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 61 | 'django.middleware.common.CommonMiddleware', 62 | 'django.contrib.messages.middleware.MessageMiddleware', 63 | ] 64 | 65 | SITE_ID = 1 66 | 67 | STATIC_URL = '/static/' 68 | MEDIA_ROOT = os.path.join(BASE_DIR, 'tests/media') 69 | MEDIA_URL = '/media/' 70 | -------------------------------------------------------------------------------- /tests/test_models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/math-a3k/django-ai/c2abc027bb47eb4a518b8daa8c0e73b2175cc59f/tests/test_models/__init__.py -------------------------------------------------------------------------------- /tests/test_models/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ExamplesConfig(AppConfig): 5 | name = 'test_models' 6 | -------------------------------------------------------------------------------- /tests/test_models/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11 on 2017-04-26 22:59 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='UserInfo', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('age', models.IntegerField(verbose_name='Age')), 21 | ('sex', models.CharField(choices=[(0, 'M'), (1, 'F')], max_length=1, verbose_name='Sex')), 22 | ('avg1', models.FloatField(blank=True, null=True, verbose_name='Average 1')), 23 | ], 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /tests/test_models/migrations/0002_populate_userinfo.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11 on 2017-05-02 23:12 3 | from __future__ import unicode_literals 4 | import numpy as np 5 | 6 | from django.db import migrations 7 | 8 | 9 | def populate_userinfos(apps, schema_editor): 10 | UserInfo = apps.get_model("test_models", "UserInfo") 11 | # Use a fixed seed for generate content 12 | np.random.seed(123456) 13 | # Size of table 14 | size = 200 15 | # Sex is ~ 70% F (0) / 30% M (1) 16 | sex = np.random.binomial(1, 0.7, size) # 200 Bernoullies :) 17 | # Age is around 30, mostly between 25 and 35 18 | age = np.floor(np.random.normal(30, 2, size=(size,))) 19 | # Average 1 is a metric normally distributed around 10 with a std dev of 5 20 | avg1 = np.random.normal(10, 5, size=(size,)) 21 | # Create the objects in the Model 22 | uis = [] 23 | for i in range(0, size): 24 | uis.append(UserInfo(age=age[i], sex=sex[i], avg1=avg1[i])) 25 | UserInfo.objects.bulk_create(uis) 26 | 27 | 28 | def unpopuplate_userinfos(apps, schema_editor): 29 | UserInfo = apps.get_model("test_models", "UserInfo") 30 | UserInfo.objects.all().delete() 31 | 32 | 33 | class Migration(migrations.Migration): 34 | 35 | dependencies = [ 36 | ('test_models', '0001_initial'), 37 | ] 38 | 39 | operations = [ 40 | migrations.RunPython(populate_userinfos, 41 | unpopuplate_userinfos), 42 | ] 43 | -------------------------------------------------------------------------------- /tests/test_models/migrations/0003_add_avg_times_clusters.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-10-09 03:20 3 | from __future__ import unicode_literals 4 | import numpy as np 5 | 6 | from django.db import migrations, models 7 | 8 | 9 | def populate_avg_times(apps, schema_editor): 10 | UserInfo = apps.get_model("test_models", "UserInfo") 11 | # Use a fixed seed for generate content 12 | np.random.seed(123456) 13 | # Generate the clusters (from previous migrations total_size is 200) 14 | y0 = np.random.multivariate_normal([20, 20], [[2, 0], [0, 0.1]], 15 | size=50) 16 | y1 = np.random.multivariate_normal([20, 20], [[0.1, 0], [0, 2]], 17 | size=50) 18 | y2 = np.random.multivariate_normal([25, 25], [[2, -1.5], [-1.5, 2]], 19 | size=50) 20 | y3 = np.random.multivariate_normal([16, 16], [[0.5, 0], [0, 0.5]], 21 | size=50) 22 | y = np.vstack([y0, y1, y2, y3]) 23 | uis = UserInfo.objects.all() 24 | # Update the objects in the Model 25 | for index, ui in enumerate(uis): 26 | ui.avg_time_pages_a = y[index][0] 27 | ui.avg_time_logged = y[index][1] 28 | ui.save(update_fields=['avg_time_logged', 'avg_time_pages_a']) 29 | 30 | 31 | def unpopulate_avg_times(apps, schema_editor): 32 | """ 33 | This is for making the migration reversible, it doesn't do anything 34 | because the fields will be removed after by the reverse of AddField. 35 | """ 36 | pass 37 | 38 | 39 | class Migration(migrations.Migration): 40 | 41 | dependencies = [ 42 | ('test_models', '0002_populate_userinfo'), 43 | ] 44 | 45 | operations = [ 46 | migrations.AddField( 47 | model_name='userinfo', 48 | name='avg_time_pages_a', 49 | field=models.FloatField( 50 | blank=True, null=True, 51 | verbose_name='Average Time spent on Pages A'), 52 | ), 53 | migrations.AddField( 54 | model_name='userinfo', 55 | name='avg_time_logged', 56 | field=models.FloatField( 57 | blank=True, null=True, 58 | verbose_name='Average Weekly Time Logged In'), 59 | ), 60 | migrations.RunPython(populate_avg_times, unpopulate_avg_times), 61 | ] 62 | -------------------------------------------------------------------------------- /tests/test_models/migrations/0004_userinfo_cluster_1.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-10-28 09:40 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('test_models', '0003_add_avg_times_clusters'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='userinfo', 17 | name='cluster_1', 18 | field=models.CharField(blank=True, max_length=1, null=True, verbose_name='Cluster 1'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /tests/test_models/migrations/0005_rename_avg_time_logged.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-11-04 07:13 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('test_models', '0004_userinfo_cluster_1'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RenameField( 16 | model_name='userinfo', 17 | old_name='avg_time_logged', 18 | new_name='avg_time_pages', 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /tests/test_models/migrations/0006_create_mystatmodel_and_alter_ui_avg_time_pages.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-12-04 05:07 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import jsonfield.fields 7 | import picklefield.fields 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('test_models', '0005_rename_avg_time_logged'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='MyStatisticalModel', 19 | fields=[ 20 | ('id', models.AutoField( 21 | auto_created=True, primary_key=True, serialize=False, 22 | verbose_name='ID')), 23 | ('name', models.CharField( 24 | max_length=100, unique=True, verbose_name='Name')), 25 | ('engine_object', picklefield.fields.PickledObjectField( 26 | blank=True, editable=False, null=True, 27 | verbose_name='Engine Object')), 28 | ('engine_object_timestamp', models.DateTimeField( 29 | blank=True, null=True, 30 | verbose_name='Engine Object Timestamp')), 31 | ('sm_type', models.SmallIntegerField( 32 | blank=True, choices=[(0, 'General'), (1, 'Classification'), 33 | (2, 'Regression')], 34 | default=0, null=True, 35 | verbose_name='Statistical Technique Type')), 36 | ('metadata', jsonfield.fields.JSONField( 37 | blank=True, default={}, null=True, 38 | verbose_name='Metadata')), 39 | ('engine_meta_iterations', models.SmallIntegerField( 40 | default=1, verbose_name='Engine Meta Iterations')), 41 | ('engine_iterations', models.SmallIntegerField( 42 | blank=True, null=True, 43 | verbose_name='Engine Iterations (Max)')), 44 | ('results_storage', models.CharField( 45 | blank=True, max_length=100, null=True, 46 | verbose_name='Results Storage')), 47 | ('counter', models.IntegerField( 48 | blank=True, default=0, null=True, 49 | verbose_name='Internal Counter')), 50 | ('counter_threshold', models.IntegerField( 51 | blank=True, null=True, 52 | verbose_name='Internal Counter Threshold')), 53 | ('threshold_actions', models.CharField( 54 | blank=True, max_length=200, null=True, 55 | verbose_name='Threshold actions')), 56 | ], 57 | ), 58 | migrations.AlterField( 59 | model_name='userinfo', 60 | name='avg_time_pages', 61 | field=models.FloatField( 62 | blank=True, null=True, verbose_name='Average Time spent on Pages'), 63 | ), 64 | ] 65 | -------------------------------------------------------------------------------- /tests/test_models/migrations/0007_userinfo2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-12-04 05:32 3 | from __future__ import unicode_literals 4 | 5 | import numpy as np 6 | 7 | from django.db import migrations, models 8 | 9 | 10 | def populate_userinfos2(apps, schema_editor): 11 | UserInfo2 = apps.get_model("test_models", "UserInfo2") 12 | # Use a fixed seed for generate content 13 | np.random.seed(123456) 14 | # Size of table 15 | size = 100 16 | # Average 2 is a metric normally distributed around 20 with a std dev of 5 17 | avg2 = np.random.normal(20, 5, size=(size,)) 18 | # Average Times spent on Pages B is a metric normally distributed around 19 | # 30 with a std dev of 5 20 | avg_time_pages_b = np.random.normal(30, 5, size=(size,)) 21 | # Create the objects in the Model 22 | uis = [] 23 | for i in range(0, size): 24 | uis.append(UserInfo2(avg_time_pages_b=avg_time_pages_b[i], 25 | avg2=avg2[i])) 26 | UserInfo2.objects.bulk_create(uis) 27 | 28 | 29 | def unpopuplate_userinfos2(apps, schema_editor): 30 | pass 31 | 32 | 33 | class Migration(migrations.Migration): 34 | 35 | dependencies = [ 36 | ('test_models', '0006_create_mystatmodel_and_alter_ui_avg_time_pages'), 37 | ] 38 | 39 | operations = [ 40 | migrations.CreateModel( 41 | name='UserInfo2', 42 | fields=[ 43 | ('id', models.AutoField( 44 | auto_created=True, primary_key=True, serialize=False, 45 | verbose_name='ID')), 46 | ('avg2', models.FloatField( 47 | blank=True, null=True, verbose_name='Average 2')), 48 | ('avg_time_pages_b', models.FloatField( 49 | blank=True, null=True, 50 | verbose_name='Average Time spent on Pages B')), 51 | ('cluster_2', models.CharField( 52 | blank=True, max_length=1, null=True, 53 | verbose_name='Cluster 1')), 54 | ], 55 | ), 56 | migrations.RunPython(populate_userinfos2, 57 | unpopuplate_userinfos2), 58 | ] 59 | -------------------------------------------------------------------------------- /tests/test_models/migrations/0008_mystatisticalmodel_has_results.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-12-05 05:57 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('test_models', '0007_userinfo2'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='mystatisticalmodel', 17 | name='has_results', 18 | field=models.BooleanField(default=True, verbose_name='Has Results?'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /tests/test_models/migrations/0009_mysupervisedlearningtechnique.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-12-05 14:43 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import jsonfield.fields 7 | import picklefield.fields 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('test_models', '0008_mystatisticalmodel_has_results'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='MySupervisedLearningTechnique', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('name', models.CharField(max_length=100, unique=True, verbose_name='Name')), 22 | ('engine_object', picklefield.fields.PickledObjectField(blank=True, editable=False, null=True, verbose_name='Engine Object')), 23 | ('engine_object_timestamp', models.DateTimeField(blank=True, null=True, verbose_name='Engine Object Timestamp')), 24 | ('sm_type', models.SmallIntegerField(blank=True, choices=[(0, 'General'), (1, 'Classification'), (2, 'Regression')], default=0, null=True, verbose_name='Statistical Technique Type')), 25 | ('metadata', jsonfield.fields.JSONField(blank=True, default={}, null=True, verbose_name='Metadata')), 26 | ('engine_meta_iterations', models.SmallIntegerField(default=1, verbose_name='Engine Meta Iterations')), 27 | ('engine_iterations', models.SmallIntegerField(blank=True, null=True, verbose_name='Engine Iterations (Max)')), 28 | ('has_results', models.BooleanField(default=True, verbose_name='Has Results?')), 29 | ('results_storage', models.CharField(blank=True, max_length=100, null=True, verbose_name='Results Storage')), 30 | ('counter', models.IntegerField(blank=True, default=0, null=True, verbose_name='Internal Counter')), 31 | ('counter_threshold', models.IntegerField(blank=True, null=True, verbose_name='Internal Counter Threshold')), 32 | ('threshold_actions', models.CharField(blank=True, max_length=200, null=True, verbose_name='Threshold actions')), 33 | ('sl_type', models.SmallIntegerField(blank=True, choices=[(0, 'Classification'), (1, 'Regression')], default=0, null=True, verbose_name='Supervised Learning Type')), 34 | ('labels_column', models.CharField(blank=True, help_text='Format: app_label.model.attribute', max_length=100, null=True, verbose_name="Labels' Column")), 35 | ], 36 | ), 37 | ] 38 | -------------------------------------------------------------------------------- /tests/test_models/migrations/0010_myunsupervisedlearningtechnique.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-12-05 15:46 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import jsonfield.fields 7 | import picklefield.fields 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('test_models', '0009_mysupervisedlearningtechnique'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='MyUnsupervisedLearningTechnique', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('name', models.CharField(max_length=100, unique=True, verbose_name='Name')), 22 | ('engine_object', picklefield.fields.PickledObjectField(blank=True, editable=False, null=True, verbose_name='Engine Object')), 23 | ('engine_object_timestamp', models.DateTimeField(blank=True, null=True, verbose_name='Engine Object Timestamp')), 24 | ('sm_type', models.SmallIntegerField(blank=True, choices=[(0, 'General'), (1, 'Classification'), (2, 'Regression')], default=0, null=True, verbose_name='Statistical Technique Type')), 25 | ('metadata', jsonfield.fields.JSONField(blank=True, default={}, null=True, verbose_name='Metadata')), 26 | ('engine_meta_iterations', models.SmallIntegerField(default=1, verbose_name='Engine Meta Iterations')), 27 | ('engine_iterations', models.SmallIntegerField(blank=True, null=True, verbose_name='Engine Iterations (Max)')), 28 | ('has_results', models.BooleanField(default=True, verbose_name='Has Results?')), 29 | ('results_storage', models.CharField(blank=True, max_length=100, null=True, verbose_name='Results Storage')), 30 | ('counter', models.IntegerField(blank=True, default=0, null=True, verbose_name='Internal Counter')), 31 | ('counter_threshold', models.IntegerField(blank=True, null=True, verbose_name='Internal Counter Threshold')), 32 | ('threshold_actions', models.CharField(blank=True, max_length=200, null=True, verbose_name='Threshold actions')), 33 | ('ul_type', models.SmallIntegerField(blank=True, choices=[(0, 'Clustering'), (1, 'Other')], default=0, null=True, verbose_name='Unsupervised Learning Type')), 34 | ], 35 | ), 36 | ] 37 | -------------------------------------------------------------------------------- /tests/test_models/migrations/0011_add_is_inferred_and_minor_tweaks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-12-20 15:34 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('test_models', '0010_myunsupervisedlearningtechnique'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='mystatisticalmodel', 17 | name='is_inferred', 18 | field=models.BooleanField( 19 | default=False, verbose_name='Is Inferred?'), 20 | ), 21 | migrations.AddField( 22 | model_name='mysupervisedlearningtechnique', 23 | name='is_inferred', 24 | field=models.BooleanField( 25 | default=False, verbose_name='Is Inferred?'), 26 | ), 27 | migrations.AddField( 28 | model_name='myunsupervisedlearningtechnique', 29 | name='is_inferred', 30 | field=models.BooleanField( 31 | default=False, verbose_name='Is Inferred?'), 32 | ), 33 | migrations.AlterField( 34 | model_name='mystatisticalmodel', 35 | name='sm_type', 36 | field=models.SmallIntegerField(blank=True, choices=[ 37 | (0, 'General / System'), 38 | (1, 'Classification'), 39 | (2, 'Regression')], 40 | default=0, null=True, 41 | verbose_name='Statistical Technique Type'), 42 | ), 43 | migrations.AlterField( 44 | model_name='mysupervisedlearningtechnique', 45 | name='sm_type', 46 | field=models.SmallIntegerField(blank=True, choices=[ 47 | (0, 'General / System'), 48 | (1, 'Classification'), 49 | (2, 'Regression')], 50 | default=0, null=True, 51 | verbose_name='Statistical Technique Type'), 52 | ), 53 | migrations.AlterField( 54 | model_name='myunsupervisedlearningtechnique', 55 | name='sm_type', 56 | field=models.SmallIntegerField(blank=True, choices=[ 57 | (0, 'General / System'), 58 | (1, 'Classification'), 59 | (2, 'Regression')], 60 | default=0, null=True, 61 | verbose_name='Statistical Technique Type'), 62 | ), 63 | ] 64 | -------------------------------------------------------------------------------- /tests/test_models/migrations/0012_mysfpt_spammablemodel.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-12-22 14:38 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('test_models', '0011_add_is_inferred_and_minor_tweaks'), 12 | ] 13 | 14 | def populate_pretraining(apps, schema_editor): 15 | SFTP = apps.get_model("test_models", "MySFPT") 16 | 17 | SFTP.objects.bulk_create([ 18 | SFTP(content="Buy Viagra Online", is_spam=True), 19 | SFTP(content="Buy Cialis Online", is_spam=True), 20 | SFTP(content="Buy Elephant Online", is_spam=True), 21 | SFTP(content="Loved your Video", is_spam=False), 22 | SFTP(content="Amazing Video", is_spam=False), 23 | SFTP(content="I agree with your comment", is_spam=False), 24 | ]) 25 | 26 | def unpopulate_pretraining(apps, schema_editor): 27 | """ 28 | Nothing to do here (as the table will be deleted) 29 | """ 30 | pass 31 | 32 | def populate_spammable_mode(apps, schema_editor): 33 | SpammableModel = apps.get_model("test_models", 34 | "SpammableModel") 35 | 36 | SpammableModel.objects.bulk_create([ 37 | SpammableModel(comment="Hi! Great Article!", is_spam=False), 38 | SpammableModel(comment="I disagree with you", is_spam=False), 39 | SpammableModel(comment="Buy a Panda Online!", is_spam=True), 40 | SpammableModel(comment="Buy a Jiraffe Online!", is_spam=True), 41 | ]) 42 | 43 | def unpopulate_spammable_model(apps, schema_editor): 44 | """ 45 | Nothing to do here (as the table will be deleted) 46 | """ 47 | pass 48 | 49 | operations = [ 50 | migrations.CreateModel( 51 | name='MySFPT', 52 | fields=[ 53 | ('id', models.AutoField( 54 | auto_created=True, 55 | primary_key=True, serialize=False, verbose_name='ID')), 56 | ('content', models.TextField(verbose_name='Content')), 57 | ('is_spam', models.BooleanField( 58 | default=False, verbose_name='Is Spam?')), 59 | ], 60 | options={ 61 | 'verbose_name_plural': 'Spam Filter Pre-Trainings', 62 | 'verbose_name': 'Spam Filter Pre-Training', 63 | 'abstract': False, 64 | }, 65 | ), 66 | migrations.CreateModel( 67 | name='SpammableModel', 68 | fields=[ 69 | ('id', models.AutoField( 70 | auto_created=True, 71 | primary_key=True, serialize=False, verbose_name='ID')), 72 | ('is_spam', models.BooleanField( 73 | default=False, 74 | help_text='If the object is Spam', 75 | verbose_name='Is Spam?')), 76 | ('is_misclassified', models.BooleanField( 77 | default=False, 78 | help_text=('If the object has been misclassified by ' 79 | 'the Spam Filter'), 80 | verbose_name='Is Misclassified?')), 81 | ('is_revised', models.BooleanField( 82 | default=False, 83 | help_text=('If the object classification has been revised ' 84 | 'by a Human'), 85 | verbose_name='Is Revised?')), 86 | ('comment', models.TextField( 87 | verbose_name='Comment')), 88 | ], 89 | options={ 90 | 'abstract': False, 91 | }, 92 | ), 93 | migrations.RunPython( 94 | populate_pretraining, unpopulate_pretraining 95 | ), 96 | migrations.RunPython( 97 | populate_spammable_mode, unpopulate_spammable_model 98 | ), 99 | ] 100 | -------------------------------------------------------------------------------- /tests/test_models/migrations/0013_nullboolean_for_sp_is_spam.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2017-12-29 15:32 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('test_models', '0012_mysfpt_spammablemodel'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='spammablemodel', 17 | name='is_spam', 18 | field=models.NullBooleanField( 19 | help_text='If the object is Spam', verbose_name='Is Spam?'), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /tests/test_models/migrations/0014_add_new_fields_from_am.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.5 on 2018-01-10 17:25 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('test_models', '0013_nullboolean_for_sp_is_spam'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='mysupervisedlearningtechnique', 17 | name='cv_folds', 18 | field=models.SmallIntegerField( 19 | blank=True, 20 | help_text='Quantity of Folds to be used in Cross Validation', 21 | null=True, verbose_name='Cross Validation Folds'), 22 | ), 23 | migrations.AddField( 24 | model_name='mysupervisedlearningtechnique', 25 | name='cv_is_enabled', 26 | field=models.BooleanField( 27 | default=True, 28 | help_text='Enable Cross Validation', 29 | verbose_name='Cross Validation is Enabled?'), 30 | ), 31 | migrations.AddField( 32 | model_name='mysupervisedlearningtechnique', 33 | name='cv_metric', 34 | field=models.CharField( 35 | blank=True, 36 | help_text='Metric to be evaluated in Cross Validation', 37 | max_length=20, 38 | null=True, 39 | verbose_name='Cross Validation Metric'), 40 | ), 41 | migrations.AddField( 42 | model_name='mysupervisedlearningtechnique', 43 | name='pretraining', 44 | field=models.CharField( 45 | blank=True, 46 | help_text=('Django Model containing the pre-training dataset ' 47 | 'in the"app_label.model" format, i.e. ' 48 | '"examples.SFPTEnron"'), 49 | max_length=100, 50 | null=True, 51 | verbose_name='Pre-Training dataset'), 52 | ), 53 | migrations.AlterField( 54 | model_name='mystatisticalmodel', 55 | name='sm_type', 56 | field=models.SmallIntegerField( 57 | blank=True, 58 | choices=[(0, 'General'), (1, 'Classification'), 59 | (2, 'Regression')], 60 | default=0, 61 | null=True, 62 | verbose_name='Statistical Technique Type'), 63 | ), 64 | migrations.AlterField( 65 | model_name='mysupervisedlearningtechnique', 66 | name='sm_type', 67 | field=models.SmallIntegerField( 68 | blank=True, 69 | choices=[(0, 'General'), (1, 'Classification'), 70 | (2, 'Regression')], 71 | default=0, 72 | null=True, 73 | verbose_name='Statistical Technique Type'), 74 | ), 75 | migrations.AlterField( 76 | model_name='myunsupervisedlearningtechnique', 77 | name='sm_type', 78 | field=models.SmallIntegerField( 79 | blank=True, 80 | choices=[(0, 'General'), (1, 'Classification'), 81 | (2, 'Regression')], 82 | default=0, 83 | null=True, 84 | verbose_name='Statistical Technique Type'), 85 | ), 86 | ] 87 | -------------------------------------------------------------------------------- /tests/test_models/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/math-a3k/django-ai/c2abc027bb47eb4a518b8daa8c0e73b2175cc59f/tests/test_models/migrations/__init__.py -------------------------------------------------------------------------------- /tests/test_models/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from django.db import models 4 | 5 | from django_ai.base.models import ( 6 | StatisticalModel, 7 | SupervisedLearningTechnique, 8 | UnsupervisedLearningTechnique, 9 | ) 10 | from django_ai.systems.spam_filtering.models import ( 11 | IsSpammable, 12 | SpamFilterPreTraining, 13 | ) 14 | 15 | 16 | class UserInfo(models.Model): 17 | """ 18 | Example User Information Model 19 | """ 20 | SEX_CHOICES = ((0, "M"), (1, "F")) 21 | 22 | age = models.IntegerField("Age") 23 | sex = models.CharField("Sex", choices=SEX_CHOICES, max_length=1) 24 | avg1 = models.FloatField("Average 1", blank=True, null=True) 25 | avg_time_pages = models.FloatField("Average Time spent on Pages", 26 | blank=True, null=True) 27 | avg_time_pages_a = models.FloatField("Average Time spent on Pages A", 28 | blank=True, null=True) 29 | cluster_1 = models.CharField("Cluster 1", max_length=1, 30 | blank=True, null=True) 31 | 32 | def __unicode__(self): 33 | return(self) 34 | 35 | 36 | class UserInfo2(models.Model): 37 | """ 38 | User Information Model 2 39 | """ 40 | avg2 = models.FloatField("Average 2", blank=True, null=True) 41 | avg_time_pages_b = models.FloatField("Average Time spent on Pages B", 42 | blank=True, null=True) 43 | cluster_2 = models.CharField("Cluster 1", max_length=1, 44 | blank=True, null=True) 45 | 46 | def __unicode__(self): 47 | return(self) 48 | 49 | 50 | class MyStatisticalModel(StatisticalModel): 51 | class Meta: 52 | app_label = "test_models" 53 | 54 | 55 | class MySupervisedLearningTechnique(SupervisedLearningTechnique): 56 | class Meta: 57 | app_label = "test_models" 58 | 59 | 60 | class MyUnsupervisedLearningTechnique(UnsupervisedLearningTechnique): 61 | class Meta: 62 | app_label = "test_models" 63 | 64 | 65 | class SpammableModel(IsSpammable): 66 | SPAM_FILTER = "Spam Filter for tests" 67 | SPAMMABLE_FIELD = "comment" 68 | 69 | comment = models.TextField("Comment") 70 | 71 | 72 | class MySFPT(SpamFilterPreTraining): 73 | pass 74 | -------------------------------------------------------------------------------- /tests/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals, absolute_import 3 | 4 | from django.conf.urls import (url, include) 5 | from django.conf import settings 6 | from django.conf.urls.static import static 7 | from django.contrib import admin 8 | 9 | urlpatterns = [ 10 | url(r'^admin/', admin.site.urls), 11 | url(r'^django-ai/', include('django_ai.base.urls')), 12 | url(r'^bayesian_networks/', include('django_ai.bayesian_networks.urls')), 13 | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 14 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | {py35}-{django-111} 4 | {py35}-{django-20} 5 | 6 | [base] 7 | deps = 8 | bayespy==0.5.6 9 | django-picklefield==1.0.0 10 | django-nested-admin==3.0.20 11 | graphviz==0.6 12 | jsonfield==2.0.2 13 | numpy<1.14 14 | Pillow==4.2.1 15 | pyparsing==2.2.0 16 | scikit-learn>=0.19.1 17 | coverage==4.4.1 18 | mock>=1.0.1 19 | flake8>=2.1.0 20 | tox>=1.7.0 21 | codecov>=2.0.0 22 | 23 | [testenv] 24 | setenv = 25 | PYTHONPATH = {toxinidir}:{toxinidir}/django_ai 26 | PYTHONHASHSEED = 0 27 | commands = coverage run --source django_ai runtests.py 28 | deps = 29 | # -r{toxinidir}/requirements_test.txt 30 | {[base]deps} 31 | django-111: Django>=1.11,<2.0 32 | django-20: Django<=2.0,<2.1 33 | basepython = 34 | py35: python3.5 35 | 36 | [flake8] 37 | exclude = .tox,./build 38 | filename = *.py 39 | ignore = E402 40 | --------------------------------------------------------------------------------