{ "info": { "author": "", "author_email": "", "bugtrack_url": null, "classifiers": [], "description": "# response_validation_app\n\nImplements a simple unsupervised method for classifying student short to medium sized responses to questions.\n\n## Installation\n\nThis was developed in Python 3.6. \n\nIt may be installed as a package from the pypi repository, using [pip](https://pip.pypa.io/en/stable/):\n\n```bash\npip install response-validator\n```\n\n## Development\nAfter cloning the repository, you can install the repo in editable mode, as so:\n\n```bash\npip install -e .\n```\nNote that this step will download several NLTK corpora, silently, and add them to the deployed tree.\n\nAdditional functionality for running algorithm tests, etc. can be enabled by installing additional libraries:\n\n```bash\npip install -r requirements.txt\n```\n\n## Usage\n\n### Development\n\nIn order to persist the book vocabulary data between invocations, the Flask\nserver needs the `DATA_DIR` setting to contain a path pointing to an existing\ndirectory. This can be set in several ways.\n\n1. Pass a key-value command line argument:\n\n\n`python -m validator.app DATA_DIR=data`\n\n\n2. set the `VALIDATOR_SETTINGS` environment variable to the path of a file\nthat contains the `DATA_DIR` setting:\n\n`VALIDATOR_SETTINGS=data/dev.cfg python -m validator.app`\n\nWhere the contents of dev.cfg is:\n\n```\nDATA_DIR=data\n```\nand the directory `data` exists.\n\n3. use gunicorn, provide arguments to app factory:\n\n\n`gunicorn 'validator.app:create_app(DATA_DIR=\"data\")'`\n\n\n4. Use gunicorn, with an environment variable pointing to a config file:\n\n\n`VALIDATOR_SETTINGS=../data/dev.cfg gunicorn \"validator.app:create_app()\"`\n\nNote that this one can get confusing with relative paths, since flask uses the\ndirectory the app is imported from (in this case, `validator`) as the config\npath when interpreting environment variables, while paths inside such files\nwill be based on the python current working directory. When in doubt, use full\npaths:\n\n`VALIDATOR_SETTINGS=\"$PWD/data/dev.cfg\" gunicorn \"validator.app:create_app()\"`\n\n### Production\nThe recommended production method for deployment is to use a WSGI compliant\nserver, such as gunicorn:\n\n```bash\npip install gunicorn gevent\ngunicorn -k gevent -b 5000 \"validator.app:create_app(DATA_DIR='/var/lib/validator/data')\" \n```\n\nIdeally, use a socket, and place nginx or other webserver in front of flask, for https termination, if nothing else.\n\n```bash\ngunicorn -k gevent --bind /run/gunicorn.sock \"validator.app:create_app(DATA_DIR='/var/lib/validator/data')\"\n```\n## API\n\n### Response Validation\nThe main route for the app is /validate, which accepts a plaintext response (`response`) that will be checked. It can also accept a number of optional arguments:\n\n- `uid` (e.g., '1000@1', default None): This is the uid for the question pertaining to the response. The uid is used to compute domain-specific and module-specific vocabulary to aid in the classification process.\nIff the version of the question specified is not available, any version of the same qid (question id without the version, e.g. 1000) will be used. \n\n- `remove_stopwords` (True or False, default True): Whether or not stopwords (e.g., 'the', 'and', etc) will be removed from the response. This is generally advised since these words carry little predictive value.\n\n- `tag_numeric` (True, False or auto, default auto): Whether numerical values will be tagged (e.g., 123.7 is tagged with a special 'numeric_type_float' identifier). While there are certainly responses for which this would be helpful, a large amount of student garbage consists of random number pressing which limits the utility of this option. Auto enables a mode that only does numeric tag processing if the question this response pertains to (as fond via the uid above) requires a numeric answer.\n\n- `spelling_correction` (True, False or auto, default auto): Whether the app will attempt to correct misspellings. This is done by identifying unknown words in the response and seeing if a closely related known word can be substituted. Currently, the app only attempts spelling correction on words of at least 5 characters in length and only considers candidate words that are within an edit distance of 2 from the misspelled word. When running in `auto` mode, the app will attempt to determine validity without spelling correction. Only if that is not valid, will it attempt to reassess validity with spelling correction.\n\n- `spell_correction_max` (integer, default 10): Limit spelling corrections applied to this number.\n\n- `remove_nonwords` (True or False, default True): Words that are not recognized (after possibly attempting spelling correction) are flagged with a special 'nonsense_word' tag. This is done primarily to combat keyboard mashes (e.g., 'asdfljasdfk') that make a large percentage of invalid student responses.\n\nOnce the app is running, you can send requests using curl, requests, etc. Here is an example using Python's requests library:\n\nHere an example of how to call things using the Python requests library (assuming that the app is running on the default local development port):\n\n```python\nimport json\nimport requests\nparams = {'response': 'This is my answar to the macromolecules question nitrogenous awerawfsfs'\n 'uid': '100@2',\n 'remove_stopwords': True,\n 'tag_numeric=True': False,\n 'spelling_correction': True,\n 'remove_nonwords': True}\nr = requests.get('http://127.0.0.1:5000/validate', params=params)\nprint(json.dumps(r.json(), indent=2))\n{\n \"bad_word_count\": 1,\n \"common_word_count\": 3,\n \"computation_time\": 0.013212919235229492,\n \"domain_word_count\": 1,\n \"inner_product\": 1.5999999999999996,\n \"innovation_word_count\": 0,\n \"intercept\": 1,\n \"lazy_math_evaluation\": true,\n \"num_spelling_correction\": 2,\n \"option_word_count\": 0,\n \"processed_response\": \"answer macromolecules question nitrogenous nonsense_word\",\n \"remove_nonwords\": true,\n \"remove_stopwords\": true,\n \"response\": \"This is my answar to the macromolecules question nitrogenous awerawfsfs\",\n \"spelling_correction\": true,\n \"spelling_correction_used\": true,\n \"stem_word_count\": 0,\n \"tag_numeric\": \"auto\",\n \"tag_numeric_input\": \"auto\",\n \"uid_found\": true,\n \"uid_used\": \"100@7\",\n \"valid\": true,\n \"version\": \"2.4.0\"\n}\n```\n\nAs you can see from these results, a number of features are taken into account\nwhen determining the potential validity of the students response: the words in\nthe response itself, the words from the associated question (stem words) and\nits answers (option words), the words in the textbook associated with this\nassignment (domain words), and the words in the textbook whose first appearance\nis on the page associated with this question (innovation words). Various other\nfeatures (presence or absence of math, spelling correction, stop word\nelimination, etc) are also applied. These tests depend on vocabularies being loaded\nfor each exercise.\n\n## Service APIs\nSee details in API.md\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/openstax/response-validator", "keywords": "", "license": "AGPL, See also LICENSE.txt", "maintainer": "", "maintainer_email": "info@cnx.org", "name": "response-validator", "package_url": "https://pypi.org/project/response-validator/", "platform": "", "project_url": "https://pypi.org/project/response-validator/", "project_urls": { "Homepage": "https://github.com/openstax/response-validator" }, "release_url": "https://pypi.org/project/response-validator/5.0.1/", "requires_dist": [ "flask (>=1.0.2)", "flask-cors", "pandas", "nltk", "symspellpy", "sklearn", "PyYAML", "requests", "pytest ; extra == 'test'", "coverage ; extra == 'test'", "vcrpy ; extra == 'test'" ], "requires_python": "", "summary": "Openstax response validator server", "version": "5.0.1", "yanked": false, "yanked_reason": null }, "last_serial": 8183356, "releases": { "2.0.0": [ { "comment_text": "", "digests": { "md5": "c0142c6639dabd72a2504436aac63e43", "sha256": "cc793b01499939960fb169413f32cd688a4281d9373266dc84e9944799279ef4" }, "downloads": -1, "filename": "response_validator-2.0.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "c0142c6639dabd72a2504436aac63e43", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 6969150, "upload_time": "2019-05-13T13:37:32", "upload_time_iso_8601": "2019-05-13T13:37:32.163560Z", "url": "https://files.pythonhosted.org/packages/5a/71/7ab1d1e32dab3487d31181e2ac37f15db4ea6c9b92753260e3f4a0094b4f/response_validator-2.0.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "6825817806504255c9f400559793cb35", "sha256": "e9fb82d14249c0f0cce4a87f61536756168307354854614c5db3b7966975b2a3" }, "downloads": -1, "filename": "response-validator-2.0.0.tar.gz", "has_sig": false, "md5_digest": "6825817806504255c9f400559793cb35", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 27020, "upload_time": "2019-05-13T13:37:35", "upload_time_iso_8601": "2019-05-13T13:37:35.055727Z", "url": "https://files.pythonhosted.org/packages/c8/bc/6c8b4008b7dfcf3bbb15b4c259faaff0454f024b98d3efdd300eb7ea88c6/response-validator-2.0.0.tar.gz", "yanked": false, "yanked_reason": null } ], "2.1.0": [ { "comment_text": "", "digests": { "md5": "7a11c4f87b873fb5fa7c61eafeb4e916", "sha256": "3a7eea9a464f0799cde9d0624669f1133098eeaa99b32cb7e299c1f9c9c981d9" }, "downloads": -1, "filename": "response_validator-2.1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "7a11c4f87b873fb5fa7c61eafeb4e916", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 35948270, "upload_time": "2019-08-13T19:35:06", "upload_time_iso_8601": "2019-08-13T19:35:06.796253Z", "url": "https://files.pythonhosted.org/packages/be/10/ebae861f967a2dd1f63ce4bc342ee4c763b109765a45664b7da8d4946024/response_validator-2.1.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "2.2.1": [ { "comment_text": "", "digests": { "md5": "300f82a36ee0d562c608698f9ee4905c", "sha256": "b3ec9436319eab7228ec8775d6b9c6826023fd765ffadc53da155bcceea3f233" }, "downloads": -1, "filename": "response_validator-2.2.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "300f82a36ee0d562c608698f9ee4905c", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 39218992, "upload_time": "2019-09-03T20:59:58", "upload_time_iso_8601": "2019-09-03T20:59:58.100314Z", "url": "https://files.pythonhosted.org/packages/02/b6/ea36b745b3a558feafa20a86c93b8fa8c149d5e61734a918148e4e0deb03/response_validator-2.2.1-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "2.3.0": [ { "comment_text": "", "digests": { "md5": "3c934604355b60861e5802f353eaceed", "sha256": "05519e09baaf1b445c58cada90fa2d3a2a7e1bbaaf3cbb1abd21fb476a219f1e" }, "downloads": -1, "filename": "response_validator-2.3.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "3c934604355b60861e5802f353eaceed", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 36476236, "upload_time": "2019-09-30T21:28:31", "upload_time_iso_8601": "2019-09-30T21:28:31.465647Z", "url": "https://files.pythonhosted.org/packages/e1/cc/9bf495bf939adfc34d0f654c939d86650102459280cf63c3909c0b7bc632/response_validator-2.3.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "3.0.1": [ { "comment_text": "", "digests": { "md5": "ba477fb102ca9f9e335932932d87743a", "sha256": "3ed5b26ee57e1a3fc347c98fd747413a06d3f9375125b332cd040ec0a16d7093" }, "downloads": -1, "filename": "response_validator-3.0.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "ba477fb102ca9f9e335932932d87743a", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 34248459, "upload_time": "2019-10-28T21:51:52", "upload_time_iso_8601": "2019-10-28T21:51:52.918941Z", "url": "https://files.pythonhosted.org/packages/f0/44/594672ac6df33743b016beb47395bfbebcaf9bb4af23acef4aa19f9a179e/response_validator-3.0.1-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "3.0.2": [ { "comment_text": "", "digests": { "md5": "f3cee1543d4e677686c610298c363515", "sha256": "346fdadf0f14775c17896c069ba253ecf64bd4bfc21dd21c0ac65e6f679c5e91" }, "downloads": -1, "filename": "response_validator-3.0.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "f3cee1543d4e677686c610298c363515", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 34248524, "upload_time": "2019-11-11T14:35:16", "upload_time_iso_8601": "2019-11-11T14:35:16.517890Z", "url": "https://files.pythonhosted.org/packages/13/85/7fe4a0da9c9e29e28b47f3710889de0c2a5e15a9c19d131cd4bbce876966/response_validator-3.0.2-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "3.0.3": [ { "comment_text": "", "digests": { "md5": "270017a980667b2d5a286dd9dbe41205", "sha256": "f3006df4be1229905dc51a56368721c4172072c6e212ce2e080544ef4405b75a" }, "downloads": -1, "filename": "response_validator-3.0.3-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "270017a980667b2d5a286dd9dbe41205", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 34248542, "upload_time": "2020-01-17T22:14:46", "upload_time_iso_8601": "2020-01-17T22:14:46.364756Z", "url": "https://files.pythonhosted.org/packages/8e/80/605666df1b4a6c7cd8739e5d0f4a92dd171eb40fe3da46e61bf9ffac8868/response_validator-3.0.3-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "3.1.0": [ { "comment_text": "", "digests": { "md5": "94329fbe01b965e6aa0ee24f351cd008", "sha256": "cc62f004aa9d1cfeed9b9acb9e04489a75311b2ade2752caa737eaa849e2c1d2" }, "downloads": -1, "filename": "response_validator-3.1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "94329fbe01b965e6aa0ee24f351cd008", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 34248605, "upload_time": "2020-02-17T18:05:10", "upload_time_iso_8601": "2020-02-17T18:05:10.249563Z", "url": "https://files.pythonhosted.org/packages/f6/21/c7aed91fe27a7768a47f203f99737151a915690b01d599d81b7d81602008/response_validator-3.1.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "4.0.0": [ { "comment_text": "", "digests": { "md5": "8ce77e7cabc26a087f35cb8f17081a12", "sha256": "eb5340ac99bbc648062a7cb358d797b319111620bf689dddfae7abcbdae130f5" }, "downloads": -1, "filename": "response_validator-4.0.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "8ce77e7cabc26a087f35cb8f17081a12", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 34248799, "upload_time": "2020-08-07T19:02:01", "upload_time_iso_8601": "2020-08-07T19:02:01.682985Z", "url": "https://files.pythonhosted.org/packages/af/a9/b10198ab9179ec5b695d2432a9affed093aaae398219844a2b8613d21432/response_validator-4.0.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "5.0.0": [ { "comment_text": "", "digests": { "md5": "567774576f9437dfbdfd9dc1d1707d06", "sha256": "54f8992f9d3497d028376fdbfa832f6705c515cfcd10aaa53f0f7f4b740a0ded" }, "downloads": -1, "filename": "response_validator-5.0.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "567774576f9437dfbdfd9dc1d1707d06", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 34248399, "upload_time": "2020-08-24T18:41:00", "upload_time_iso_8601": "2020-08-24T18:41:00.280525Z", "url": "https://files.pythonhosted.org/packages/19/86/d9f58dfa86baa80befb747ce35811992b9358386bbc9bd7a625b5d8e83ed/response_validator-5.0.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "5.0.1": [ { "comment_text": "", "digests": { "md5": "62119f4c05963f9ffe6c6f78b11ca564", "sha256": "a58919cdcd8890525b179864eddb467c281fc8b27a9a2062a38873fa77eeb229" }, "downloads": -1, "filename": "response_validator-5.0.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "62119f4c05963f9ffe6c6f78b11ca564", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 34248421, "upload_time": "2020-09-14T16:58:48", "upload_time_iso_8601": "2020-09-14T16:58:48.283577Z", "url": "https://files.pythonhosted.org/packages/48/cc/b2eed0739cda0eda6f0adb0d2d3666b61b652497d760ba89f4feffb09a58/response_validator-5.0.1-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "62119f4c05963f9ffe6c6f78b11ca564", "sha256": "a58919cdcd8890525b179864eddb467c281fc8b27a9a2062a38873fa77eeb229" }, "downloads": -1, "filename": "response_validator-5.0.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "62119f4c05963f9ffe6c6f78b11ca564", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 34248421, "upload_time": "2020-09-14T16:58:48", "upload_time_iso_8601": "2020-09-14T16:58:48.283577Z", "url": "https://files.pythonhosted.org/packages/48/cc/b2eed0739cda0eda6f0adb0d2d3666b61b652497d760ba89f4feffb09a58/response_validator-5.0.1-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "vulnerabilities": [] }