├── .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 |
--------------------------------------------------------------------------------
/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 | Cluster |
7 | Size |
8 | {% for col in original.metadata.columns %}
9 | {{ col }} |
10 | {% endfor %}
11 |
12 |
13 | {% for label, mean in clusters_means.items|dictsort:0 %}
14 |
15 | {{ label }} |
16 | {{ clusters_sizes|get_item:label }} |
17 | {% for col_val in mean %}
18 | {{ col_val|stringformat:".3f" }} |
19 | {% endfor %}
20 |
21 | {% endfor %}
22 |
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 |
32 |
33 |
34 |
Post a Comment
35 |
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 |
--------------------------------------------------------------------------------