<NME> views.py
<BEF> import json
import random

from django.shortcuts import HttpResponse, render
from deck.models import Deck, card_to_dict, CARDS, JOKERS
from django.db import transaction

def doc_page(request):
    c = request.COOKIES.get('v', random.choice([1,2]))
    response = render(request, 'docs.html', {'v':int(c)})
    response.set_cookie("v", c)
    return response

def _get_request_var(request, key, default=1):
    if request.method == 'POST':
        return request.POST.get(key, default)
        return request.GET.get(key, default)

def get_jokers_enabled(request):
    j = _get_request_var(request, 'jokers_enabled', 'false')
    return j=='true'

def shuffle(request, key=''):
    return new_deck(request, key, True)
    remaining = _get_request_var(request, 'remaining')
    if isinstance(remaining, str) and key:
        if remaining.lower() == 'true':
            # Shuffle remaining cards only
            try:
                deck = Deck.objects.get(key=key)
            except Deck.DoesNotExist:
                return deck_id_does_not_exist()
            random.shuffle(deck.stack)
            deck.shuffled = True
            deck.save()

            resp = {'success': True, 'deck_id': deck.key, 'remaining': len(deck.stack), 'shuffled': deck.shuffled}
            response = HttpResponse(json.dumps(resp), content_type="application/json")
        try:
            deck = Deck.objects.get(key=key)
            deck.piles = {}
        except Deck.DoesNotExist:
            deck_id_does_not_exist()
    else: #creating a new deck
    deck_cards = _get_request_var(request, 'cards', None)
    jokers_enabled = get_jokers_enabled(request)
    if deck_count > 20:
        response = HttpResponse(
            json.dumps({'success': False, 'error': 'The max number of Decks is 20.'}),
            content_type="application/json"
        )
        response['Access-Control-Allow-Origin'] = '*'
        return response
    if key: #we are shuffling an existing deck
        print("we are here 1")
        try:
            deck = Deck.objects.get(key=key)
            deck.piles = {}
            if jokers_enabled is None:
                jokers_enabled = deck.include_jokers
        except Deck.DoesNotExist:
            print("we are here 3")
            return deck_id_does_not_exist()
    else: #creating a new deck
        deck = Deck()
        deck.deck_count = deck_count
    deck.open_new(deck_cards, jokers_enabled)
    deck.shuffled = False
    if shuffle:
        random.shuffle(deck.stack)
        deck.shuffled = True
    deck.save()  # save the deck_count.

    resp = {'success': True, 'deck_id': deck.key, 'remaining': len(deck.stack), 'shuffled': deck.shuffled}

    response = HttpResponse(json.dumps(resp), content_type="application/json")
    response['Access-Control-Allow-Origin'] = '*'
    return response


def deck_info(request, key=0):
    try:
        deck = Deck.objects.get(key=key)
    except Deck.DoesNotExist:
        return deck_id_does_not_exist()

    resp = {'success': True, 'deck_id': deck.key, 'remaining': len(deck.stack), 'shuffled': deck.shuffled}
    response = HttpResponse(json.dumps(resp), content_type="application/json")
    response['Access-Control-Allow-Origin'] = '*'
    return response

@transaction.atomic
def draw(request, key=None):
    jokers_enabled = get_jokers_enabled(request)
    success = True
    card_count = int(_get_request_var(request, 'count'))
    if not key:
        deck = Deck()
        deck.deck_count = int(_get_request_var(request, 'deck_count'))
        deck.open_new(jokers_enabled=jokers_enabled)
        random.shuffle(deck.stack)
        deck.shuffled = True
        deck.save()
    else:
        try:
            deck = Deck.objects.get(key=key)
        except Deck.DoesNotExist:
            return deck_id_does_not_exist()
            
    if card_count > len(deck.stack):
        success = False
    cards = deck.stack[0:card_count]
    deck.stack = deck.stack[card_count:]
    deck.save()

    a = []
    for card in cards:
        a.append(card_to_dict(card))

    if not success:
        resp = {
            'success': success,
            'deck_id': deck.key,
            'cards': a,
            'remaining': len(deck.stack),
            'error': 'Not enough cards remaining to draw %s additional' % str(card_count)
        }
    else:
        resp = {'success': success, 'deck_id': deck.key, 'cards': a, 'remaining': len(deck.stack)}

    response = HttpResponse(json.dumps(resp), content_type="application/json")
    response['Access-Control-Allow-Origin'] = '*'
    return response

def return_to_deck(request, key):
    try:
        deck = Deck.objects.get(key=key)
    except Deck.DoesNotExist:
        return deck_id_does_not_exist()
    
    cards_in_use = deck.stack[:] # make a copy

    if deck.piles:
        for k in deck.piles:  # iterate through all the piles and list cards in use for specified pile.
            for card in deck.piles[k]:
                cards_in_use.append(card)

    cards_specified = _get_request_var(request, 'cards', None)
    valid_cards = deck.deck_contents or (CARDS, CARDS + JOKERS)[deck.include_jokers]

    if cards_specified is None:
        # Return all free cards to the deck
        cards_specified = [x for x in valid_cards if x not in cards_in_use]
    else:
        cards_specified = cards_specified.upper()
        cards_specified = [x for x in cards_specified.split(',') if x not in cards_in_use and x in valid_cards]  # check that the cards has been drawn (not in use) and is a valid card from this deck
    
    deck.stack.extend(cards_specified)
    deck.save()

    resp = {'success': True, 'deck_id': deck.key, 'remaining': len(deck.stack)}

    response = HttpResponse(json.dumps(resp), content_type="application/json")
    response['Access-Control-Allow-Origin'] = '*'
    return response

def return_pile_to_deck(request, key, pile):
    try:
        deck = Deck.objects.get(key=key)
    except Deck.DoesNotExist:
        return deck_id_does_not_exist()

    cards_specified = _get_request_var(request, 'cards', None)
    
    if deck.piles and pile in deck.piles:
        if cards_specified is None:
            # Return all cards from the pile to the deck
            deck.stack.extend(deck.piles[pile])
            deck.piles[pile] = []
        else:
            cards_specified = cards_specified.upper()
            cards_specified = [x for x in cards_specified.split(',') if x in deck.piles[pile]]  # check that the cards are in the pile
            deck.stack.extend(cards_specified) # put specified cards back into deck.stack
            deck.piles[pile] = [x for x in deck.piles[pile] if x not in cards_specified] # remove sepcified cards
        deck.save()

        piles = {}
        for k in deck.piles:  # iterate through all the piles and get the count.
            r = len(deck.piles[k])
            piles[k] = {"remaining": r}
        resp = {'success': True, 'deck_id': deck.key, 'remaining': len(deck.stack), 'piles': piles}
        response = HttpResponse(json.dumps(resp), content_type="application/json")
        response['Access-Control-Allow-Origin'] = '*'
        return response
    else:
        response = HttpResponse(
            json.dumps({
                'success': False, 'error': 'The pile, %s does not exist.' % pile
            }),
            content_type="application/json",
            status=404
            )
        response['Access-Control-Allow-Origin'] = '*'
        return response

def add_to_pile(request, key, pile):
    try:
        deck = Deck.objects.get(key=key)
    except Deck.DoesNotExist:
        return deck_id_does_not_exist()

    jokers_enabled = deck.include_jokers

    cards_specified = _get_request_var(request, 'cards', None)
    if cards_specified is None:
        response = HttpResponse(
            json.dumps({'success': False, 'error': 'You must specify cards to add to the pile.'}),
            content_type="application/json",
            status=404
        )
        response['Access-Control-Allow-Origin'] = '*'
        return response
    
    cards_specified = cards_specified.upper()
    # Only allow real cards

    all_cards = (CARDS, CARDS + JOKERS)[jokers_enabled]
    cards_specified = [x for x in cards_specified.split(',') if x not in deck.stack and x in all_cards]  # check that the cards has been drawn and is a valid card code.

    if not deck.piles:
        deck.piles = {}

    print(deck.piles)
    for key in deck.piles:  # iterate through all the piles and remove any specified cards from those piles.
        p = deck.piles[key]  # times like these that I question if I should have just made piles a model instead of a json field...
        for card in cards_specified: 
            if card in p:
                p.remove(card)

    try:  # try to add to the pile
        deck.piles[pile].extend(cards_specified)
    except KeyError as e:  # the pile is brand new
        deck.piles[pile] = cards_specified  # add the specified cards to the new pile
    deck.save()

    piles = {}
    for k in deck.piles:
        piles[k] = {'remaining': len(deck.piles[k])}

    resp = {'success': True, 'deck_id': deck.key, 'remaining': len(deck.stack), 'piles': piles}
    response = HttpResponse(json.dumps(resp), content_type="application/json")
    response['Access-Control-Allow-Origin'] = '*'
    return response


def deck_id_does_not_exist():
    response = HttpResponse(
        json.dumps({'success': False, 'error': 'Deck ID does not exist.'}),
        content_type="application/json",
        status=404
    )
    response['Access-Control-Allow-Origin'] = '*'
    return response


def shuffle_pile(request, key, pile):
    try:
        deck = Deck.objects.get(key=key)
    except Deck.DoesNotExist:
        return deck_id_does_not_exist()

    piles = {}
    random.shuffle(deck.piles[pile])
    deck.save()

    for k in deck.piles:  # iterate through all the piles and get the count.
        r = len(deck.piles[k])
        piles[k] = {"remaining": r}

    resp = {'success': True, 'deck_id': deck.key, 'remaining': len(deck.stack), 'piles': piles}
    response = HttpResponse(json.dumps(resp), content_type="application/json")
    response['Access-Control-Allow-Origin'] = '*'
    return response


def list_cards_in_pile(request, key, pile):
    try:
        deck = Deck.objects.get(key=key)
    except Deck.DoesNotExist:
        return deck_id_does_not_exist()

    piles = {}

    for k in deck.piles:  # iterate through all the piles and list cards for specified pile.
        r = len(deck.piles[k])
        if k != pile:
            piles[k] = {"remaining": r}
        else:
            a = []
            for card in deck.piles[k]:
                a.append(card_to_dict(card))
            piles[k] = {"remaining": r, "cards": a}

    resp = {'success': True, 'deck_id': deck.key, 'remaining': len(deck.stack), 'piles': piles}
    response = HttpResponse(json.dumps(resp), content_type="application/json")
    response['Access-Control-Allow-Origin'] = '*'
    return response


def draw_from_pile(request, key, pile, location=""):
    jokers_enabled = get_jokers_enabled(request)
    try:
        deck = Deck.objects.get(key=key)
    except Deck.DoesNotExist:
        return deck_id_does_not_exist()

    cards = _get_request_var(request, 'cards', None)
    cards_in_response = []

    p = deck.piles[pile]  # times like these that I question if I should have just made piles a model instead of a json field...

    if cards:
        # Ignore case
        cards = cards.upper()
        # Only allow real cards
        cards = [x for x in cards.split(',') if x in CARDS+JOKERS]
   
        for card in cards:
            try:
                p.remove(card)
                cards_in_response.append(card)
            except:
                response = HttpResponse(
                    json.dumps({
                        'success': False, 'error': 'The pile, %s does not contain the requested cards.' % pile
                    }),
                    content_type="application/json",
                    status=404
                )
                response['Access-Control-Allow-Origin'] = '*'
                return response
    else:
        card_count = int(_get_request_var(request, 'count'))
        if card_count > len(p):
            response = HttpResponse(
                json.dumps({
                    'success': False, 'error': 'Not enough cards remaining to draw %s additional' % str(card_count)
                }),
                content_type="application/json",
                status=404
            )
            response['Access-Control-Allow-Origin'] = '*'
            return response
        if location.lower() == "random":  # draw random cards
            for i in range(card_count):
                random_card_index = random.randint(0, len(p)-1)
                random_card = p.pop(random_card_index)
                cards_in_response.append(random_card)
        elif location.lower() == "bottom":  # draw from the bottom of the pile
            cards_in_response = p[0:card_count]
            p = p[card_count:len(p)]
        else:  # draw cards from the top of the pile
            cards_in_response = p[-card_count:]
            p = p[:-card_count]

    deck.piles[pile] = p
    deck.save()
    
    a = []

    for card in cards_in_response:
        a.append(card_to_dict(card))

    piles = {}
    for k in deck.piles:
        piles[k] = {"remaining": len(deck.piles[k])}

    resp = {'success': True, 'deck_id': deck.key, 'cards': a, 'piles': piles}
    response = HttpResponse(json.dumps(resp), content_type="application/json")
    response['Access-Control-Allow-Origin'] = '*'
    return response

<MSG> Merge pull request #86 from LoganDan/issue-74-consider-jokers-when-shuffling

Issue #74: 
<DFF> @@ -17,8 +17,11 @@ def _get_request_var(request, key, default=1):
         return request.GET.get(key, default)
 
 def get_jokers_enabled(request):
-    j = _get_request_var(request, 'jokers_enabled', 'false')
-    return j=='true'
+    j = _get_request_var(request, 'jokers_enabled')
+    if isinstance(j, str):
+        if j.lower() == 'true': return True
+        if j.lower() == 'false': return False
+    return
 
 def shuffle(request, key=''):
     return new_deck(request, key, True)
@@ -39,6 +42,8 @@ def new_deck(request, key='', shuffle=False):
         try:
             deck = Deck.objects.get(key=key)
             deck.piles = {}
+            if jokers_enabled is None:
+                jokers_enabled = deck.include_jokers
         except Deck.DoesNotExist:
             deck_id_does_not_exist()
     else: #creating a new deck

{'commit': 'diff --git a/deck/migrations/0005_include_jokers.py b/deck/migrations/0005_include_jokers.py\nnew file mode 100644\nindex 0000000..af9018f\n--- /dev/null\n+++ b/deck/migrations/0005_include_jokers.py\n@@ -0,0 +1,29 @@\n+# Generated by Django 2.2.10 on 2020-08-23 14:48\n+\n+import django.contrib.auth.validators\n+from django.db import migrations, models\n+\n+\n+class Migration(migrations.Migration):\n+\n+    dependencies = [\n+        (\'deck\', \'0004_deck_shuffled\'),\n+    ]\n+\n+    operations = [\n+        migrations.AddField(\n+            model_name=\'deck\',\n+            name=\'include_jokers\',\n+            field=models.BooleanField(default=False),\n+        ),\n+        migrations.AlterField(\n+            model_name=\'user\',\n+            name=\'last_name\',\n+            field=models.CharField(blank=True, max_length=150, verbose_name=\'last name\'),\n+        ),\n+        migrations.AlterField(\n+            model_name=\'user\',\n+            name=\'username\',\n+            field=models.CharField(error_messages={\'unique\': \'A user with that username already exists.\'}, help_text=\'Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.\', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name=\'username\'),\n+        ),\n+    ]\ndiff --git a/deck/models.py b/deck/models.py\nindex dc05eaf..d4ff224 100644\n--- a/deck/models.py\n+++ b/deck/models.py\n@@ -26,7 +26,7 @@ CARDS = [\'AS\', \'2S\', \'3S\', \'4S\', \'5S\', \'6S\', \'7S\', \'8S\', \'9S\', \'0S\', \'JS\', \'QS\',\n          \'AD\', \'2D\', \'3D\', \'4D\', \'5D\', \'6D\', \'7D\', \'8D\', \'9D\', \'0D\', \'JD\', \'QD\', \'KD\',\n          \'AC\', \'2C\', \'3C\', \'4C\', \'5C\', \'6C\', \'7C\', \'8C\', \'9C\', \'0C\', \'JC\', \'QC\', \'KC\',\n          \'AH\', \'2H\', \'3H\', \'4H\', \'5H\', \'6H\', \'7H\', \'8H\', \'9H\', \'0H\', \'JH\', \'QH\', \'KH\']\n-JOKERS = ["X1", "X2"]\n+JOKERS = [\'X1\', \'X2\']\n \n SUITS = {\'S\': \'SPADES\', \'D\': \'DIAMONDS\', \'H\': \'HEARTS\', \'C\': \'CLUBS\', \'1\': \'BLACK\', \'2\': \'RED\'}\n VALUES = {\'A\': \'ACE\', \'J\': \'JACK\', \'Q\': \'QUEEN\', \'K\': \'KING\', \'0\': \'10\', \'X\': \'JOKER\'}\n@@ -39,18 +39,20 @@ class Deck(models.Model):\n     piles = JSONField(null=True, blank=True) \n     deck_contents = JSONField(null=True, blank=True) #All the cards that should be included when shuffling and whatnot\n     shuffled = models.BooleanField(default=False)\n+    include_jokers = models.BooleanField(default=False)\n     \n     def open_new(self, cards_used=None, jokers_enabled=False):\n+        self.include_jokers = False if jokers_enabled is None else jokers_enabled\n         stack = []\n         if cards_used is None: # use all the cards\n             if self.deck_contents is None:\n-                cards = (CARDS, CARDS + JOKERS)[jokers_enabled]\n+                cards = (CARDS, CARDS + JOKERS)[self.include_jokers]\n             else:\n                 cards = self.deck_contents[:]\n         else: # use a subset of a standard deck\n             cards_used = cards_used.upper()\n             # Only allow real cards\n-            valid_cards = (CARDS, CARDS + JOKERS)[jokers_enabled]\n+            valid_cards = (CARDS, CARDS + JOKERS)[self.include_jokers]\n             cards = [x for x in valid_cards if x in cards_used.split(\',\')]\n             self.deck_contents = cards[:]  # save the subset for future shuffles\n \ndiff --git a/deck/views.py b/deck/views.py\nindex 88fdffb..6a24599 100644\n--- a/deck/views.py\n+++ b/deck/views.py\n@@ -17,8 +17,11 @@ def _get_request_var(request, key, default=1):\n         return request.GET.get(key, default)\n \n def get_jokers_enabled(request):\n-    j = _get_request_var(request, \'jokers_enabled\', \'false\')\n-    return j==\'true\'\n+    j = _get_request_var(request, \'jokers_enabled\')\n+    if isinstance(j, str):\n+        if j.lower() == \'true\': return True\n+        if j.lower() == \'false\': return False\n+    return\n \n def shuffle(request, key=\'\'):\n     return new_deck(request, key, True)\n@@ -39,6 +42,8 @@ def new_deck(request, key=\'\', shuffle=False):\n         try:\n             deck = Deck.objects.get(key=key)\n             deck.piles = {}\n+            if jokers_enabled is None:\n+                jokers_enabled = deck.include_jokers\n         except Deck.DoesNotExist:\n             deck_id_does_not_exist()\n     else: #creating a new deck\n', 'commit_subject': 'Merge pull request #86 from LoganDan/issue-74-consider-jokers-when-shuffling', 'repo_name': 'crobertsbmw/deckofcards', 'committer': {'name': 'GitHub', 'email': '1505422b2465e9a84f6fdfaa161078890c593f06@github.com', 'time_sec': '1598534138', 'tz_offset': '-300', 'date': {'seconds': '1598534138'}}, 'commit_tree': '254894f39dd46427af4dde99d2540104e0e091aa', 'commit_message': 'Merge pull request #86 from LoganDan/issue-74-consider-jokers-when-shuffling\n\nIssue #74: ', 'language_name': 'HTML', 'license': 'mit', 'hunks': "@@ -17,8 +17,11 @@ def _get_request_var(request, key, default=1):\n         return request.GET.get(key, default)\n \n def get_jokers_enabled(request):\n-    j = _get_request_var(request, 'jokers_enabled', 'false')\n-    return j=='true'\n+    j = _get_request_var(request, 'jokers_enabled')\n+    if isinstance(j, str):\n+        if j.lower() == 'true': return True\n+        if j.lower() == 'false': return False\n+    return\n \n def shuffle(request, key=''):\n     return new_deck(request, key, True)\n@@ -39,6 +42,8 @@ def new_deck(request, key='', shuffle=False):\n         try:\n             deck = Deck.objects.get(key=key)\n             deck.piles = {}\n+            if jokers_enabled is None:\n+                jokers_enabled = deck.include_jokers\n         except Deck.DoesNotExist:\n             deck_id_does_not_exist()\n     else: #creating a new deck\n", 'addition_count': 7, 'deletion_count': 2, 'src_file': 'a/deck/views.py', 'tgt_file': 'b/deck/views.py', 'file_extension': '.py', 'before_file': 'import json\nimport random\n\nfrom django.shortcuts import HttpResponse, render\nfrom deck.models import Deck, card_to_dict, CARDS, JOKERS\nfrom django.db import transaction\n\ndef doc_page(request):\n    c = request.COOKIES.get(\'v\', random.choice([1,2]))\n    response = render(request, \'docs.html\', {\'v\':int(c)})\n    response.set_cookie("v", c)\n    return response\n\ndef _get_request_var(request, key, default=1):\n    if request.method == \'POST\':\n        return request.POST.get(key, default)\n        return request.GET.get(key, default)\n\ndef get_jokers_enabled(request):\n    j = _get_request_var(request, \'jokers_enabled\', \'false\')\n    return j==\'true\'\n\ndef shuffle(request, key=\'\'):\n    return new_deck(request, key, True)\n    remaining = _get_request_var(request, \'remaining\')\n    if isinstance(remaining, str) and key:\n        if remaining.lower() == \'true\':\n            # Shuffle remaining cards only\n            try:\n                deck = Deck.objects.get(key=key)\n            except Deck.DoesNotExist:\n                return deck_id_does_not_exist()\n            random.shuffle(deck.stack)\n            deck.shuffled = True\n            deck.save()\n\n            resp = {\'success\': True, \'deck_id\': deck.key, \'remaining\': len(deck.stack), \'shuffled\': deck.shuffled}\n            response = HttpResponse(json.dumps(resp), content_type="application/json")\n        try:\n            deck = Deck.objects.get(key=key)\n            deck.piles = {}\n        except Deck.DoesNotExist:\n            deck_id_does_not_exist()\n    else: #creating a new deck\n    deck_cards = _get_request_var(request, \'cards\', None)\n    jokers_enabled = get_jokers_enabled(request)\n    if deck_count > 20:\n        response = HttpResponse(\n            json.dumps({\'success\': False, \'error\': \'The max number of Decks is 20.\'}),\n            content_type="application/json"\n        )\n        response[\'Access-Control-Allow-Origin\'] = \'*\'\n        return response\n    if key: #we are shuffling an existing deck\n        print("we are here 1")\n        try:\n            deck = Deck.objects.get(key=key)\n            deck.piles = {}\n            if jokers_enabled is None:\n                jokers_enabled = deck.include_jokers\n        except Deck.DoesNotExist:\n            print("we are here 3")\n            return deck_id_does_not_exist()\n    else: #creating a new deck\n        deck = Deck()\n        deck.deck_count = deck_count\n    deck.open_new(deck_cards, jokers_enabled)\n    deck.shuffled = False\n    if shuffle:\n        random.shuffle(deck.stack)\n        deck.shuffled = True\n    deck.save()  # save the deck_count.\n\n    resp = {\'success\': True, \'deck_id\': deck.key, \'remaining\': len(deck.stack), \'shuffled\': deck.shuffled}\n\n    response = HttpResponse(json.dumps(resp), content_type="application/json")\n    response[\'Access-Control-Allow-Origin\'] = \'*\'\n    return response\n\n\ndef deck_info(request, key=0):\n    try:\n        deck = Deck.objects.get(key=key)\n    except Deck.DoesNotExist:\n        return deck_id_does_not_exist()\n\n    resp = {\'success\': True, \'deck_id\': deck.key, \'remaining\': len(deck.stack), \'shuffled\': deck.shuffled}\n    response = HttpResponse(json.dumps(resp), content_type="application/json")\n    response[\'Access-Control-Allow-Origin\'] = \'*\'\n    return response\n\n@transaction.atomic\ndef draw(request, key=None):\n    jokers_enabled = get_jokers_enabled(request)\n    success = True\n    card_count = int(_get_request_var(request, \'count\'))\n    if not key:\n        deck = Deck()\n        deck.deck_count = int(_get_request_var(request, \'deck_count\'))\n        deck.open_new(jokers_enabled=jokers_enabled)\n        random.shuffle(deck.stack)\n        deck.shuffled = True\n        deck.save()\n    else:\n        try:\n            deck = Deck.objects.get(key=key)\n        except Deck.DoesNotExist:\n            return deck_id_does_not_exist()\n            \n    if card_count > len(deck.stack):\n        success = False\n    cards = deck.stack[0:card_count]\n    deck.stack = deck.stack[card_count:]\n    deck.save()\n\n    a = []\n    for card in cards:\n        a.append(card_to_dict(card))\n\n    if not success:\n        resp = {\n            \'success\': success,\n            \'deck_id\': deck.key,\n            \'cards\': a,\n            \'remaining\': len(deck.stack),\n            \'error\': \'Not enough cards remaining to draw %s additional\' % str(card_count)\n        }\n    else:\n        resp = {\'success\': success, \'deck_id\': deck.key, \'cards\': a, \'remaining\': len(deck.stack)}\n\n    response = HttpResponse(json.dumps(resp), content_type="application/json")\n    response[\'Access-Control-Allow-Origin\'] = \'*\'\n    return response\n\ndef return_to_deck(request, key):\n    try:\n        deck = Deck.objects.get(key=key)\n    except Deck.DoesNotExist:\n        return deck_id_does_not_exist()\n    \n    cards_in_use = deck.stack[:] # make a copy\n\n    if deck.piles:\n        for k in deck.piles:  # iterate through all the piles and list cards in use for specified pile.\n            for card in deck.piles[k]:\n                cards_in_use.append(card)\n\n    cards_specified = _get_request_var(request, \'cards\', None)\n    valid_cards = deck.deck_contents or (CARDS, CARDS + JOKERS)[deck.include_jokers]\n\n    if cards_specified is None:\n        # Return all free cards to the deck\n        cards_specified = [x for x in valid_cards if x not in cards_in_use]\n    else:\n        cards_specified = cards_specified.upper()\n        cards_specified = [x for x in cards_specified.split(\',\') if x not in cards_in_use and x in valid_cards]  # check that the cards has been drawn (not in use) and is a valid card from this deck\n    \n    deck.stack.extend(cards_specified)\n    deck.save()\n\n    resp = {\'success\': True, \'deck_id\': deck.key, \'remaining\': len(deck.stack)}\n\n    response = HttpResponse(json.dumps(resp), content_type="application/json")\n    response[\'Access-Control-Allow-Origin\'] = \'*\'\n    return response\n\ndef return_pile_to_deck(request, key, pile):\n    try:\n        deck = Deck.objects.get(key=key)\n    except Deck.DoesNotExist:\n        return deck_id_does_not_exist()\n\n    cards_specified = _get_request_var(request, \'cards\', None)\n    \n    if deck.piles and pile in deck.piles:\n        if cards_specified is None:\n            # Return all cards from the pile to the deck\n            deck.stack.extend(deck.piles[pile])\n            deck.piles[pile] = []\n        else:\n            cards_specified = cards_specified.upper()\n            cards_specified = [x for x in cards_specified.split(\',\') if x in deck.piles[pile]]  # check that the cards are in the pile\n            deck.stack.extend(cards_specified) # put specified cards back into deck.stack\n            deck.piles[pile] = [x for x in deck.piles[pile] if x not in cards_specified] # remove sepcified cards\n        deck.save()\n\n        piles = {}\n        for k in deck.piles:  # iterate through all the piles and get the count.\n            r = len(deck.piles[k])\n            piles[k] = {"remaining": r}\n        resp = {\'success\': True, \'deck_id\': deck.key, \'remaining\': len(deck.stack), \'piles\': piles}\n        response = HttpResponse(json.dumps(resp), content_type="application/json")\n        response[\'Access-Control-Allow-Origin\'] = \'*\'\n        return response\n    else:\n        response = HttpResponse(\n            json.dumps({\n                \'success\': False, \'error\': \'The pile, %s does not exist.\' % pile\n            }),\n            content_type="application/json",\n            status=404\n            )\n        response[\'Access-Control-Allow-Origin\'] = \'*\'\n        return response\n\ndef add_to_pile(request, key, pile):\n    try:\n        deck = Deck.objects.get(key=key)\n    except Deck.DoesNotExist:\n        return deck_id_does_not_exist()\n\n    jokers_enabled = deck.include_jokers\n\n    cards_specified = _get_request_var(request, \'cards\', None)\n    if cards_specified is None:\n        response = HttpResponse(\n            json.dumps({\'success\': False, \'error\': \'You must specify cards to add to the pile.\'}),\n            content_type="application/json",\n            status=404\n        )\n        response[\'Access-Control-Allow-Origin\'] = \'*\'\n        return response\n    \n    cards_specified = cards_specified.upper()\n    # Only allow real cards\n\n    all_cards = (CARDS, CARDS + JOKERS)[jokers_enabled]\n    cards_specified = [x for x in cards_specified.split(\',\') if x not in deck.stack and x in all_cards]  # check that the cards has been drawn and is a valid card code.\n\n    if not deck.piles:\n        deck.piles = {}\n\n    print(deck.piles)\n    for key in deck.piles:  # iterate through all the piles and remove any specified cards from those piles.\n        p = deck.piles[key]  # times like these that I question if I should have just made piles a model instead of a json field...\n        for card in cards_specified: \n            if card in p:\n                p.remove(card)\n\n    try:  # try to add to the pile\n        deck.piles[pile].extend(cards_specified)\n    except KeyError as e:  # the pile is brand new\n        deck.piles[pile] = cards_specified  # add the specified cards to the new pile\n    deck.save()\n\n    piles = {}\n    for k in deck.piles:\n        piles[k] = {\'remaining\': len(deck.piles[k])}\n\n    resp = {\'success\': True, \'deck_id\': deck.key, \'remaining\': len(deck.stack), \'piles\': piles}\n    response = HttpResponse(json.dumps(resp), content_type="application/json")\n    response[\'Access-Control-Allow-Origin\'] = \'*\'\n    return response\n\n\ndef deck_id_does_not_exist():\n    response = HttpResponse(\n        json.dumps({\'success\': False, \'error\': \'Deck ID does not exist.\'}),\n        content_type="application/json",\n        status=404\n    )\n    response[\'Access-Control-Allow-Origin\'] = \'*\'\n    return response\n\n\ndef shuffle_pile(request, key, pile):\n    try:\n        deck = Deck.objects.get(key=key)\n    except Deck.DoesNotExist:\n        return deck_id_does_not_exist()\n\n    piles = {}\n    random.shuffle(deck.piles[pile])\n    deck.save()\n\n    for k in deck.piles:  # iterate through all the piles and get the count.\n        r = len(deck.piles[k])\n        piles[k] = {"remaining": r}\n\n    resp = {\'success\': True, \'deck_id\': deck.key, \'remaining\': len(deck.stack), \'piles\': piles}\n    response = HttpResponse(json.dumps(resp), content_type="application/json")\n    response[\'Access-Control-Allow-Origin\'] = \'*\'\n    return response\n\n\ndef list_cards_in_pile(request, key, pile):\n    try:\n        deck = Deck.objects.get(key=key)\n    except Deck.DoesNotExist:\n        return deck_id_does_not_exist()\n\n    piles = {}\n\n    for k in deck.piles:  # iterate through all the piles and list cards for specified pile.\n        r = len(deck.piles[k])\n        if k != pile:\n            piles[k] = {"remaining": r}\n        else:\n            a = []\n            for card in deck.piles[k]:\n                a.append(card_to_dict(card))\n            piles[k] = {"remaining": r, "cards": a}\n\n    resp = {\'success\': True, \'deck_id\': deck.key, \'remaining\': len(deck.stack), \'piles\': piles}\n    response = HttpResponse(json.dumps(resp), content_type="application/json")\n    response[\'Access-Control-Allow-Origin\'] = \'*\'\n    return response\n\n\ndef draw_from_pile(request, key, pile, location=""):\n    jokers_enabled = get_jokers_enabled(request)\n    try:\n        deck = Deck.objects.get(key=key)\n    except Deck.DoesNotExist:\n        return deck_id_does_not_exist()\n\n    cards = _get_request_var(request, \'cards\', None)\n    cards_in_response = []\n\n    p = deck.piles[pile]  # times like these that I question if I should have just made piles a model instead of a json field...\n\n    if cards:\n        # Ignore case\n        cards = cards.upper()\n        # Only allow real cards\n        cards = [x for x in cards.split(\',\') if x in CARDS+JOKERS]\n   \n        for card in cards:\n            try:\n                p.remove(card)\n                cards_in_response.append(card)\n            except:\n                response = HttpResponse(\n                    json.dumps({\n                        \'success\': False, \'error\': \'The pile, %s does not contain the requested cards.\' % pile\n                    }),\n                    content_type="application/json",\n                    status=404\n                )\n                response[\'Access-Control-Allow-Origin\'] = \'*\'\n                return response\n    else:\n        card_count = int(_get_request_var(request, \'count\'))\n        if card_count > len(p):\n            response = HttpResponse(\n                json.dumps({\n                    \'success\': False, \'error\': \'Not enough cards remaining to draw %s additional\' % str(card_count)\n                }),\n                content_type="application/json",\n                status=404\n            )\n            response[\'Access-Control-Allow-Origin\'] = \'*\'\n            return response\n        if location.lower() == "random":  # draw random cards\n            for i in range(card_count):\n                random_card_index = random.randint(0, len(p)-1)\n                random_card = p.pop(random_card_index)\n                cards_in_response.append(random_card)\n        elif location.lower() == "bottom":  # draw from the bottom of the pile\n            cards_in_response = p[0:card_count]\n            p = p[card_count:len(p)]\n        else:  # draw cards from the top of the pile\n            cards_in_response = p[-card_count:]\n            p = p[:-card_count]\n\n    deck.piles[pile] = p\n    deck.save()\n    \n    a = []\n\n    for card in cards_in_response:\n        a.append(card_to_dict(card))\n\n    piles = {}\n    for k in deck.piles:\n        piles[k] = {"remaining": len(deck.piles[k])}\n\n    resp = {\'success\': True, \'deck_id\': deck.key, \'cards\': a, \'piles\': piles}\n    response = HttpResponse(json.dumps(resp), content_type="application/json")\n    response[\'Access-Control-Allow-Origin\'] = \'*\'\n    return response\n', 'message': 'Merge pull request #86 from LoganDan/issue-74-consider-jokers-when-shuffling\n\nIssue #74: ', 'author_name': 'GitHub', 'lang': 'py'}