diff --git a/README.rst b/README.rst index 98eb9f3..1a70e0e 100644 --- a/README.rst +++ b/README.rst @@ -30,6 +30,33 @@ The service leverages the Anki project for flashcard management and display. Getting Started *************** +Running The Application using Docker +==================================== + +.. code-block:: + + # Build the docker image: + make docker_build + + # Spin up the containers: + make dev.up + + # Enter the shell: + make app-shell + python manage.py shell + + # Import function and test it: + from flashcards.apps.cards.anki import main + +Start fetching content locally +============================== +checkout https://github.com/edx/ai-aside/tree/ashultz0/extractor into src +provision a user in the devstack folder with the lms running: ./provision-ida-user.sh flashcards flashcards 3000 +run make install-local in ai-aside +restart lms so that the aside actually loads +You must create an XBlockAsidesConfig (admin URL: /admin/lms_xblock/xblockasidesconfig/). This model has a list of blocks you do not want asides to apply to that can be left alone, and an enabled setting that unsurprisingly should be True. + + Developing ========== diff --git a/flashcards/apps/cards/anki.py b/flashcards/apps/cards/anki.py new file mode 100644 index 0000000..e88316e --- /dev/null +++ b/flashcards/apps/cards/anki.py @@ -0,0 +1,55 @@ +""" +Implementation of AnkiConnect to create cards +""" +from flashcards.apps.cards.cardgen import cards_from_block_id +import genanki + + +def create_anki_cards(openai_data): + """ + Generates anki cards from data + """ + my_model = genanki.Model( + 1607392319, + 'Simple Model', + fields=[ + {'name': 'Question'}, + {'name': 'Answer'}, + ], + templates=[ + { + 'name': 'Card 1', + 'qfmt': '{{Question}}', + 'afmt': '{{FrontSide}}
{{Answer}}', + }, + ]) + + my_deck = genanki.Deck( + 2059400111, + 'Demo Deck 1') + + rows = openai_data.split('\n') + for row in rows: + question, answer = row.split(',', 1) + + my_note = genanki.Note( + model=my_model, + fields=[question, answer]) + + my_deck.add_note(my_note) + + genanki.Package(my_deck).write_to_file('demo_output.apkg') + + +def main(): + """ + Master function that gets a response from openai and passes the result to Anki + """ + result = cards_from_block_id('course-v1:edX+DemoX+Demo_Course', + 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@867dddb6f55d410caaa9c1eb9c6743ec') + # TODO: Insert some kind of data validation here to make sure openai sent back something nice + result = result.replace('\t', '') + create_anki_cards(result) + + +main() diff --git a/flashcards/apps/cards/cardgen.py b/flashcards/apps/cards/cardgen.py index 07cec05..6874006 100644 --- a/flashcards/apps/cards/cardgen.py +++ b/flashcards/apps/cards/cardgen.py @@ -7,8 +7,6 @@ openai.api_key = OPENAI_API_KEY -# provision a user in devstack provision-ida-user.sh flashcards flashcards 3000 - def get_client(oauth_base_url=settings.LMS_ROOT_URL): """ diff --git a/flashcards/utils.py b/flashcards/utils.py deleted file mode 100644 index 82f2722..0000000 --- a/flashcards/utils.py +++ /dev/null @@ -1,197 +0,0 @@ -""" -OpenAI integration utilities. - -Integration with OpenAI to generate 'flashcards' in csv form -based on course content -""" - -import openai -from django.conf import settings - -openai.api_key = settings.OPENAI_API_KEY - - -content_prompt = """ -The content for this course is the following - -What’s living in your food? Many of the foods that we consume daily owe their distinct characteristics and flavors to microbes, specifically through a biochemical process of fermentation (using bacteria, fungi, and other microorganisms to produce diverse foods). Gourmands and everyday consumers can quickly name some of the most popular fermented foods we consume—beer, yogurt, pickles—but, what about that coffee you drank this morning, or the chocolate bar you are saving for later? - -Through hands-on, at-home exercises, you will experiment with your food to grow your own microbial environments to make mead, sourdough, tempeh, and more—and discover the important role science plays in food fermentation. In Food Fermentation: The Science of Cooking with Microbes, you will explore the history of food and beverage fermentations and how it changes and enhances flavors, aromas, and tastes. You will engage with your peers in kitchen science, discussing how and why fermentation does or does not happen and what conditions you should consider to create the right growth opportunities. - -From chemistry to microbiology to your dinner plate, this course will analyze the role of microbes in production, preservation, and enhancement of diverse foods across a variety of culinary traditions. You will study the following types of fermentations: - -Lesson 1: Bread and Mead -Lesson 2: Lactic Acid Bacteria -Lesson 3: Beer and Sourdough -Lesson 4: Wine and Vinegar -Lesson 5: Filamentous Fungi -Lesson 6: Aged Meat and Cheese -Lesson 7: Chocolate and Coffee -""" # noqa - -course_content = """ -ROBERTO KOLTER: While humans have been preparing and consuming -microbial foods for thousands of years, it -wasn't until relatively recent in history -that humans first visualized individual microbes. -The story of how this came to happen takes us back -just a little over 300 years, to the mid-1600s, to the town of Delft -in the Netherlands, beautifully drawn here in this painting -by Johannes Vermeer. -Amongst the Delft canals, there lived a contemporary -of Vermeer, an enterprising textile merchant -by the name of Antonie van Leeuwenhoek. -He was driven by a great curiosity to explore the world around him. -And as part of his trade, he wanted to be -better able to assess the quality of the cloths he bought and sold. -To achieve closer and closer inspection of the threads, -van Leeuwenhoek learned the craft of lens grinding and polishing. -He became quite good at it, eventually producing -small spherical lenses that allowed magnifications never before -accomplished by humans. -He placed these lenses in rudimentary metal -mounts, inventing the world's first microscopes. -By placing samples in the pin-shaped holder -and viewing them through his outstanding lenses, -he was able to see dimensions no one had seen before. -To his astonishment, he discovered tiny, little forms -which, for the lack of better word, he called "little animals" -or "animalcules." -He noticed that they were of varied forms -and remarkably numerous, writing in 1683, "All the people living in -our united Netherlands are not as many as the living animals -that I carry in my mouth this very day." -Imagine if you will, that you are van Leeuwenhoek -and are seeing through one of his microscopes at a single drop of water, -collected from one of the canals of Delft. -This is what you would have seen, a remarkable array of living entities. -Some of the bigger ones are animals, indeed, -such as the rotifer that is being tracked. -But the smaller ones, swimming along rapidly, those are bacteria. -And they're a few thousands of a millimeter, -impossible to see with the naked eye. -Yet these tiny microbes are responsible for all those wonderful fermentations -that produce the beverages and foods we will be discussing in this course. -It's truly a marvelous sight to behold the microbial activity that can -be going on in a single drop of water. -But van Leeuwenhoek's discovery would remain pretty much -a curiosity for a couple of centuries. -It was not until the second half of the 19th century -that the French scientist Louis Pasteur came onto the scene -and began to truly study the activities that were catalyzed -by these tiniest of living creatures. -Driven to solve practical problems, Pasteur -became interested in studying wine production and the reasons -why wine went bad. -Being French, and wine being a key beverage in French culture, -Pasteur knew there would be great interest in his studies on this topic. -At the time, many people still believed that processes -such as the turning of grape juice into wine happened spontaneously. -This is a so-called theory of spontaneous generation of life. -Simply by having liquids such as beef broth or grape juice in contact -with air, microbes would be formed. -Pasteur elegantly debunked that theory. -First, he recognized that by heating such liquids, -he could kill all the microbes present. -In other words, he could sterilize the liquids. -Then by making gooseneck flasks containing sterile medium, -Pasteur was able to show that the medium would remain sterile, -despite it's still having contact with the outside air. -This ability to sterilize liquids allowed -him to later on put into them new microbes and study their properties. -We call this act of putting microbes into a growth medium inoculation. -Having starting materials that are sterile or largely devoid of microbes -can be a very useful practice when cooking with microbes, -so do keep that in mind. -Now, Pasteur had been trained as a chemist -and was keenly interested in understanding -the chemical transformations of matter that took place on Earth. -One such transformation is the conversion of grape juice into wine. -At the time, it was already known that the sugars in grape juice -were converted to the gas carbon dioxide, or CO2, plus alcohol, ethanol, -to be precise. -But how this conversion was carried out was not known. -Using his ability to sterilize liquids, Pasteur -showed that he could inoculate sterile grape juice with a particular microbe -and this would lead to the production of wine. -He also was able to show that other microbes, when they contaminated -the grape juice, were what caused the wine to go bad, -for example, turn it into vinegar. -The wine-making microbe was one that is going to become a close friend of ours -in this course, the yeast Saccharomyces cerevisiae. -Here, we see an image of this yeast, also known -as baker's yeast or brewer's yeast. -Yes, the same general microbe that makes wine -is our friend that we use to make beer and bread, -though there are subtle differences in the strains -that we use to produce each of those [INAUDIBLE] foods, -but we'll deal with that later. -For now, let us simply appreciate the beauty of this microbe -as we observe it magnified many thousands of times. -Pasteur's discovery led to much better practices in wine-making, -something the French, and the whole world -for that matter, very much appreciated. -It is still possible to go to restaurants in the French countryside -and see these posters acknowledging the importance of Pasteur's work. -Let me loosely translate the text around the image of Pasteur. -"Give preferences to restaurants that include -the wine in the price of the meal. -Average of human life, 59 years for a water drinker, -65 years for a wine drinker. -87% of the centenarians are wine drinkers. -Wine is the milk of all people." -And last and most importantly, a quote directly from Pasteur, -"Wine is the healthiest and most hygienic beverage." -Now we know how we as humans first came to visualize microbes -and first began to understand their role in making fermented foods. -""" - -# The prompt to ask ChatGPT to make anki cards. -anki_prompt = """ - -I want you to act as a professional Anki card creator, able to create Anki cards from the text I provide. - -Regarding the formulation of the card content, you stick to two principles: - -First, minimum information principle: The material you learn must be formulated in as simple way as it is only possible. Simplicity does not have to imply losing information and skipping the difficult part. -Second, optimize wording: The wording of your items must be optimized to make sure that in minimum time the right bulb in your brain lights up. This will reduce error rates, increase specificity, reduce response time, and help your concentration. - -Please output these cards you create in a .csv format, with the questions and answers separated by commas. - -The following is a model card-create template for you to study. - -Text: The characteristics of the Dead Sea: Salt lake located on the border between Israel and Jordan. Its shoreline is the lowest point on the Earth's surface, averaging 396 m below sea level. It is 74 km long. It is seven times as salty (30% by volume) as the ocean. Its density keeps swimmers afloat. Only simple organisms can live in its saline waters - -Where is the Dead Sea located?,On the border between Israel and Jordan -What is the lowest point on the Earth's surface?,The Dead Sea shoreline -What is the average level on which the Dead Sea is located?,400 meters (below sea level) -How long is the Dead Sea?,70 km -How much saltier is the Dead Sea as compared with the oceans?,7 times -What is the volume content of salt in the Dead Sea?,30% -Why can the Dead Sea keep swimmers afloat?,Due to high salt content -Why is the Dead Sea called Dead?,Because only simple organisms can live in it -Why only simple organisms can live in the Dead Sea?,Because of high salt content -""" # noqa - -messages = [ - {"role": "system", - "content": anki_prompt}, - {"role": "system", - "content": content_prompt + course_content}, -] - -if openai.api_key: - c3 = openai.ChatCompletion.create( - model="gpt-3.5-turbo", - messages=messages, - temperature=1.0, - ) - print(c3['choices'][0]['message']['content']) - - # c4 = openai.ChatCompletion.create( - # model="gpt-4", - # messages=messages, - # temperature=1.0, - # ) - - # print(c4) diff --git a/requirements/base.txt b/requirements/base.txt index 4801772..6ae562a 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -29,7 +29,9 @@ charset-normalizer==3.3.1 # aiohttp # requests click==8.1.7 - # via edx-django-utils + # via + # edx-django-utils + # flask coreapi==2.3.3 # via # django-rest-swagger @@ -98,6 +100,12 @@ edx-opaque-keys==2.5.1 # via edx-drf-extensions edx-rest-api-client==5.6.1 # via -r requirements/base.in +flask==3.0.0 + # via + # aqt + # flask-cors +flask-cors==4.0.0 + # via aqt frozenlist==1.4.0 # via # aiohttp @@ -115,7 +123,9 @@ jinja2==3.1.2 markdown==3.5 # via anki markupsafe==2.1.3 - # via jinja2 + # via + # jinja2 + # werkzeug multidict==6.0.4 # via # aiohttp @@ -176,8 +186,14 @@ requests[socks]==2.31.0 # social-auth-core requests-oauthlib==1.3.1 # via social-auth-core +rpds-py==0.10.6 + # via + # jsonschema + # referencing semantic-version==2.10.0 # via edx-drf-extensions +send2trash==1.8.2 + # via aqt simplejson==3.19.2 # via django-rest-swagger six==1.16.0 @@ -210,6 +226,10 @@ uritemplate==4.1.1 # via coreapi urllib3==2.0.7 # via requests +waitress==2.1.2 + # via aqt +werkzeug==3.0.0 + # via flask yarl==1.9.2 # via aiohttp zipp==3.17.0 diff --git a/requirements/dev.txt b/requirements/dev.txt index 091dbef..177c48c 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -63,6 +63,7 @@ click==8.1.7 # code-annotations # edx-django-utils # edx-lint + # flask # pip-tools click-log==0.4.0 # via @@ -194,6 +195,15 @@ filelock==3.12.4 # -r requirements/validation.txt # tox # virtualenv +flask==3.0.0 + # via + # -r requirements/validation.txt + # aqt + # flask-cors +flask-cors==4.0.0 + # via + # -r requirements/validation.txt + # aqt frozenlist==1.4.0 # via # -r requirements/validation.txt @@ -209,6 +219,7 @@ importlib-metadata==6.8.0 # -r requirements/pip-tools.txt # -r requirements/validation.txt # build + # flask # keyring # markdown # twine @@ -220,6 +231,10 @@ isort==5.12.0 # via # -r requirements/validation.txt # pylint +itsdangerous==2.1.2 + # via + # -r requirements/validation.txt + # flask itypes==1.2.0 # via # -r requirements/validation.txt @@ -239,6 +254,15 @@ jinja2==3.1.2 # code-annotations # coreschema # diff-cover + # flask +jsonschema==4.19.1 + # via + # -r requirements/validation.txt + # aqt +jsonschema-specifications==2023.7.1 + # via + # -r requirements/validation.txt + # jsonschema keyring==24.2.0 # via # -r requirements/validation.txt @@ -257,6 +281,7 @@ markupsafe==2.1.3 # via # -r requirements/validation.txt # jinja2 + # werkzeug mccabe==0.7.0 # via # -r requirements/validation.txt @@ -471,6 +496,10 @@ semantic-version==2.10.0 # via # -r requirements/validation.txt # edx-drf-extensions +send2trash==1.8.2 + # via + # -r requirements/validation.txt + # aqt simplejson==3.19.2 # via # -r requirements/validation.txt @@ -561,6 +590,14 @@ virtualenv==20.24.6 # via # -r requirements/validation.txt # tox +waitress==2.1.2 + # via + # -r requirements/validation.txt + # aqt +werkzeug==3.0.0 + # via + # -r requirements/validation.txt + # flask wheel==0.41.2 # via # -r requirements/pip-tools.txt diff --git a/requirements/doc.txt b/requirements/doc.txt index 47594aa..060c61c 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -35,6 +35,8 @@ attrs==23.1.0 # via # -r requirements/test.txt # aiohttp + # jsonschema + # referencing babel==2.13.0 # via # pydata-sphinx-theme @@ -67,6 +69,7 @@ click==8.1.7 # code-annotations # edx-django-utils # edx-lint + # flask click-log==0.4.0 # via # -r requirements/test.txt @@ -194,6 +197,15 @@ filelock==3.12.4 # -r requirements/test.txt # tox # virtualenv +flask==3.0.0 + # via + # -r requirements/test.txt + # aqt + # flask-cors +flask-cors==4.0.0 + # via + # -r requirements/test.txt + # aqt frozenlist==1.4.0 # via # -r requirements/test.txt @@ -210,6 +222,7 @@ importlib-metadata==6.8.0 # via # -r requirements/test.txt # build + # flask # keyring # markdown # sphinx @@ -222,6 +235,10 @@ isort==5.12.0 # via # -r requirements/test.txt # pylint +itsdangerous==2.1.2 + # via + # -r requirements/test.txt + # flask itypes==1.2.0 # via # -r requirements/test.txt @@ -237,7 +254,16 @@ jinja2==3.1.2 # -r requirements/test.txt # code-annotations # coreschema + # flask # sphinx +jsonschema==4.19.1 + # via + # -r requirements/test.txt + # aqt +jsonschema-specifications==2023.7.1 + # via + # -r requirements/test.txt + # jsonschema keyring==24.2.0 # via twine markdown==3.5 @@ -250,6 +276,7 @@ markupsafe==2.1.3 # via # -r requirements/test.txt # jinja2 + # werkzeug mccabe==0.7.0 # via # -r requirements/test.txt @@ -440,6 +467,10 @@ semantic-version==2.10.0 # via # -r requirements/test.txt # edx-drf-extensions +send2trash==1.8.2 + # via + # -r requirements/test.txt + # aqt simplejson==3.19.2 # via # -r requirements/test.txt @@ -554,6 +585,14 @@ virtualenv==20.24.6 # via # -r requirements/test.txt # tox +waitress==2.1.2 + # via + # -r requirements/test.txt + # aqt +werkzeug==3.0.0 + # via + # -r requirements/test.txt + # flask yarl==1.9.2 # via # -r requirements/test.txt diff --git a/requirements/production.txt b/requirements/production.txt index 3ce8770..8354883 100644 --- a/requirements/production.txt +++ b/requirements/production.txt @@ -48,6 +48,7 @@ click==8.1.7 # via # -r requirements/base.txt # edx-django-utils + # flask coreapi==2.3.3 # via # -r requirements/base.txt @@ -131,6 +132,15 @@ edx-opaque-keys==2.5.1 # edx-drf-extensions edx-rest-api-client==5.6.1 # via -r requirements/base.txt +flask==3.0.0 + # via + # -r requirements/base.txt + # aqt + # flask-cors +flask-cors==4.0.0 + # via + # -r requirements/base.txt + # aqt frozenlist==1.4.0 # via # -r requirements/base.txt @@ -167,6 +177,7 @@ markupsafe==2.1.3 # via # -r requirements/base.txt # jinja2 + # werkzeug multidict==6.0.4 # via # -r requirements/base.txt @@ -265,10 +276,19 @@ requests-oauthlib==1.3.1 # via # -r requirements/base.txt # social-auth-core +rpds-py==0.10.6 + # via + # -r requirements/base.txt + # jsonschema + # referencing semantic-version==2.10.0 # via # -r requirements/base.txt # edx-drf-extensions +send2trash==1.8.2 + # via + # -r requirements/base.txt + # aqt simplejson==3.19.2 # via # -r requirements/base.txt @@ -322,6 +342,14 @@ urllib3==2.0.7 # via # -r requirements/base.txt # requests +waitress==2.1.2 + # via + # -r requirements/base.txt + # aqt +werkzeug==3.0.0 + # via + # -r requirements/base.txt + # flask yarl==1.9.2 # via # -r requirements/base.txt diff --git a/requirements/quality.txt b/requirements/quality.txt index 8dfbaf2..381e9ae 100644 --- a/requirements/quality.txt +++ b/requirements/quality.txt @@ -56,6 +56,7 @@ click==8.1.7 # code-annotations # edx-django-utils # edx-lint + # flask click-log==0.4.0 # via # -r requirements/test.txt @@ -178,6 +179,15 @@ filelock==3.12.4 # -r requirements/test.txt # tox # virtualenv +flask==3.0.0 + # via + # -r requirements/test.txt + # aqt + # flask-cors +flask-cors==4.0.0 + # via + # -r requirements/test.txt + # aqt frozenlist==1.4.0 # via # -r requirements/test.txt @@ -203,6 +213,10 @@ isort==5.12.0 # -r requirements/quality.in # -r requirements/test.txt # pylint +itsdangerous==2.1.2 + # via + # -r requirements/test.txt + # flask itypes==1.2.0 # via # -r requirements/test.txt @@ -218,6 +232,15 @@ jinja2==3.1.2 # -r requirements/test.txt # code-annotations # coreschema + # flask +jsonschema==4.19.1 + # via + # -r requirements/test.txt + # aqt +jsonschema-specifications==2023.7.1 + # via + # -r requirements/test.txt + # jsonschema keyring==24.2.0 # via twine markdown==3.5 @@ -230,6 +253,7 @@ markupsafe==2.1.3 # via # -r requirements/test.txt # jinja2 + # werkzeug mccabe==0.7.0 # via # -r requirements/test.txt @@ -410,6 +434,10 @@ semantic-version==2.10.0 # via # -r requirements/test.txt # edx-drf-extensions +send2trash==1.8.2 + # via + # -r requirements/test.txt + # aqt simplejson==3.19.2 # via # -r requirements/test.txt @@ -495,6 +523,14 @@ virtualenv==20.24.6 # via # -r requirements/test.txt # tox +waitress==2.1.2 + # via + # -r requirements/test.txt + # aqt +werkzeug==3.0.0 + # via + # -r requirements/test.txt + # flask yarl==1.9.2 # via # -r requirements/test.txt diff --git a/requirements/test.txt b/requirements/test.txt index 45840fe..0ecdb96 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -55,6 +55,7 @@ click==8.1.7 # code-annotations # edx-django-utils # edx-lint + # flask click-log==0.4.0 # via edx-lint code-annotations==1.5.0 @@ -161,6 +162,15 @@ filelock==3.12.4 # via # tox # virtualenv +flask==3.0.0 + # via + # -r requirements/base.txt + # aqt + # flask-cors +flask-cors==4.0.0 + # via + # -r requirements/base.txt + # aqt frozenlist==1.4.0 # via # -r requirements/base.txt @@ -179,6 +189,10 @@ iniconfig==2.0.0 # via pytest isort==5.12.0 # via pylint +itsdangerous==2.1.2 + # via + # -r requirements/base.txt + # flask itypes==1.2.0 # via # -r requirements/base.txt @@ -196,6 +210,7 @@ markupsafe==2.1.3 # via # -r requirements/base.txt # jinja2 + # werkzeug mccabe==0.7.0 # via pylint multidict==6.0.4 @@ -328,10 +343,19 @@ requests-oauthlib==1.3.1 # via # -r requirements/base.txt # social-auth-core +rpds-py==0.10.6 + # via + # -r requirements/base.txt + # jsonschema + # referencing semantic-version==2.10.0 # via # -r requirements/base.txt # edx-drf-extensions +send2trash==1.8.2 + # via + # -r requirements/base.txt + # aqt simplejson==3.19.2 # via # -r requirements/base.txt @@ -405,6 +429,14 @@ urllib3==2.0.7 # requests virtualenv==20.24.6 # via tox +waitress==2.1.2 + # via + # -r requirements/base.txt + # aqt +werkzeug==3.0.0 + # via + # -r requirements/base.txt + # flask yarl==1.9.2 # via # -r requirements/base.txt diff --git a/requirements/validation.txt b/requirements/validation.txt index 005a49a..5f42ff2 100644 --- a/requirements/validation.txt +++ b/requirements/validation.txt @@ -69,6 +69,7 @@ click==8.1.7 # code-annotations # edx-django-utils # edx-lint + # flask click-log==0.4.0 # via # -r requirements/quality.txt @@ -229,6 +230,17 @@ filelock==3.12.4 # -r requirements/test.txt # tox # virtualenv +flask==3.0.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # aqt + # flask-cors +flask-cors==4.0.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # aqt frozenlist==1.4.0 # via # -r requirements/quality.txt @@ -258,6 +270,11 @@ isort==5.12.0 # -r requirements/quality.txt # -r requirements/test.txt # pylint +itsdangerous==2.1.2 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # flask itypes==1.2.0 # via # -r requirements/quality.txt @@ -278,6 +295,17 @@ jinja2==3.1.2 # -r requirements/test.txt # code-annotations # coreschema + # flask +jsonschema==4.19.1 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # aqt +jsonschema-specifications==2023.7.1 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # jsonschema keyring==24.2.0 # via # -r requirements/quality.txt @@ -296,6 +324,7 @@ markupsafe==2.1.3 # -r requirements/quality.txt # -r requirements/test.txt # jinja2 + # werkzeug mccabe==0.7.0 # via # -r requirements/quality.txt @@ -533,6 +562,11 @@ semantic-version==2.10.0 # -r requirements/quality.txt # -r requirements/test.txt # edx-drf-extensions +send2trash==1.8.2 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # aqt simplejson==3.19.2 # via # -r requirements/quality.txt @@ -636,6 +670,16 @@ virtualenv==20.24.6 # -r requirements/quality.txt # -r requirements/test.txt # tox +waitress==2.1.2 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # aqt +werkzeug==3.0.0 + # via + # -r requirements/quality.txt + # -r requirements/test.txt + # flask yarl==1.9.2 # via # -r requirements/quality.txt