Calvin Goodale, Developer - Blog
c g o o d a a l e . . c o m

Updating database onclick()

I have some basic functionality working with the schedular app I'm building in django. My most recent task was figuring out how to update an entry in my database at the click of the button so the teachers can mark a lesson timeslot as confirmed, and show the reflected schedule on both the teacher's home page and the student's homepage.

To do so, I had to first create a new view "confirm_timeslot" that would take both the teacher_slug and timeslot_slug as arguments, then updating the boolean "confirmed" field in the database to True, and then return the JsonResponse. I also had to add a new url in urls.py teacher-home/<slug:teacher_slug>/<int:timeslot_id> and point it at the confirm_timeslot view.

views.py:

def confirm_timeslot(request, teacher_slug, timeslot_id):
    selected_timeslot = TimeSlot.objects.get(id=timeslot_id)
    selected_timeslot.confirmed = True
    selected_timeslot.save(update_fields=['confirmed'])
    current_teacher = selected_timeslot.teacher_id
    return JsonResponse({'result': 'ok'})

urls.py:

path('schedular/teacher-home/<slug:teacher_slug>/<int:timeslot_id>', views.confirm_timeslot, name='confirm-timeslot')

I then had to add a button next to each timeslot entry in the html template for the teacher's homepage and add some data attributes for both the teacher_slug and timeslot_id of the request so I could pass those to my javascript/ajax call, and add the javascript function as an onclick.

teachers-home.html:

<div class="confirm-column">
  {% if not timeslot.confirmed %}
      {% csrf_token %}
      <button id="confirm-btn" onclick="confirm()" data-teacherslug="{{ slug }}" data-timeslotid="{{ timeslot.id }}">Confirm</button>
  {% else %}
      <span id="checkmark"></span>
  {% endif %}
</div>

Lastly, I had to create the confirm() javascript function which takes the passed teacher_slug and timeslot_id and send a request to the appropriate url with the variables inserted, and then update the related confirm-column div's html.

base.js:

function confirm() {
    // debugger;
    var teacherslug = $('#confirm-btn').data('teacherslug');
    var timeslotid = $('#confirm-btn').data('timeslotid')
    $.ajax({
        url: "/schedular/teacher-home/"+teacherslug+"/"+timeslotid,
        success: function(result) {
            $(".confirm-column"+timeslotid).load("/schedular/teacher-home/"+teacherslug+" .confirm-column"+timeslotid)
        }
    })
}

I learned a lot about how django and javascript/ajax can work together when solving this problem, and I'm thankful for my dev friends as well as StackOverflow for helping me when I got stuck. Looking forward to continuning to flesh out this project and will continue to post updates.


0 Comments

Django Project

This weekend I started working on a project in Django which will be a scheduling app for teachers and students. I'm building it to both increase my knowledge and create something that my brother can use for his upcoming music school. I have a basic skeleton completed, and my next steps are to add more functionality as well as work on the design and layout.


0 Comments

3D Surface Area HackerRank

Challenge:

Given a 2D array representing a square of stacks of blocks, calculate the total surface area of the blocks. 

https://www.hackerrank.com/challenges/3d-surface-area/problem

This was a fun problem that sounds more challenging than it actually is. My thought process was that we need to first calculate the height of the stack of blocks at each index as if it had no neighbors, which is 2 + (height of stack x 4), and then subtract the height of any of its neighboring stacks (or just subtract it's own height if the neighboring stack is greater than the current stack). We also need to account for edges and corners, which is handled by making sure the index of each neighboring index is greater than 0 and less than the height/width.

My solution:

def surfaceArea(A):
    total_area = 0
    for index, row in enumerate(A):
        for index2, column in enumerate(row):
            column_area = 2 + (column * 4)

            if index2 - 1 >= 0:
                left = A[index][index2 - 1]
                if left > column:
                    column_area -= column
                else:
                    column_area -= left

            if index2 + 1 <= len(A[0]) - 1:
                right = A[index][index2 + 1]
                if right > column:
                    column_area -= column
                else:
                    column_area -= right

            if index + 1 <= len(A) - 1:
                down = A[index + 1][index2]
                if down > column:
                    column_area -= column
                else:
                    column_area -= down

            if index - 1 >= 0:
                up = A[index - 1][index2]
                if up > column:
                    column_area -= column
                else:
                    column_area -= up

            total_area += column_area

    return total_area


0 Comments

HackerRank: Matrix Layer Rotation (Part 3)

I figured out that the reasons my code was failing due to Run Time Errors were (1.) I was creating a new empty matrix with the same dimensions as the given matrix and then appending the rotated arrays to that, instead of simply replacing the elements in the given matrix with the elements from the rotated arrays, and (2.) I was deleting each section from the flat array after adding it to the new matrix, which I found easier at the time, since I wouldn't have to calculate the new index of the next section. This fix was pretty simple, since I just needed to keep an incremental index going while getting the correct slices from the rotated flat array.

It took me a little while to determine the best way to handle the first part, but I finally passed 12 out of 12 test cases with the following code:

def matrixRotation(matrix, r):
    # Number of rows in the matrix
    rows = len(matrix)
    # Number of columns in the matrix
    columns = len(matrix[0])

    # New list that will contain each slice of the matrix in a new list
    matrices = []
    # The number of slices in the matrix
    matrix_num = int(min(rows, columns) / 2)
    i = 0
    while i < matrix_num:
        single_matrix = [matrix[i][i:columns-i],
                         [matrix[row][columns-1-i] for row in range(i+1, rows-i-1)],
                         matrix[rows-i-1][i:columns-i][::-1],
                         [matrix[row][i] for row in range(i+1, rows-i-1)[::-1]]]

        i += 1
        # Turn the single_matrix list of lists into a single list
        single_matrix = [mat for mat in single_matrix if mat != []]
        # Turn into a flat list
        single_matrix = list(itertools.chain(*single_matrix))
        # Rotate this flat list counter clockwise by r
        # If r is more than the length of the matrix it's more efficient to use r % len
        new_r = r % len(single_matrix)
        # Rotate the list
        single_matrix = single_matrix[new_r:] + single_matrix[:new_r]
        # Append the rotated list to the matrices list
        matrices.append(single_matrix)
    flat_matrices = list(itertools.chain(*matrices))
    m = 0
    rs = rows - 2
    cs = columns
    index = 0
    # Create new matrix from rotated matrix
    while m < matrix_num:
        # Create new top
        matrix[m][m:m+cs] = flat_matrices[index:][:cs]
        index += cs
        # Create new right side
        for k in range(rs):
            matrix[k + 1 + m][-1-m] = flat_matrices[index]
            index += 1

        # Create new bottom
        matrix[rows - 1 - m][m:m+cs] = flat_matrices[index:][:cs][::-1]
        index += cs
        # Create new left side
        for k in range(rs):
            matrix[rs - k + m][m] = flat_matrices[index]
            index += 1
        m += 1
        rs -= 2
        cs -= 2
    # Print each row as a string joined by a space
    for row in matrix:
        print(' '.join(str(num) for num in row))

It took me a bit longer than I expected to solve this problem, but as it was my first "Hard" level HackerRank problem, I'm just glad I was able to complete it. There are definitely ways of rotating each layer in place rather than creating flat arrays from each matrix layer and adding them back in, but I'm happy with my solution.


0 Comments

HackerRank: Matrix Layer Rotation (Part 2)

Continued from my last post. It looks like I'll need at least one more Blog post related to this problem, since although I found a solution, it is not optimized enough to pass all of the test cases. I'm getting timeouts on 3 of the 10 test cases in HackerRank, and I'm not quite sure where my bottlenecks are yet.

My first try had even more timeouts, but I realized that I can use mod against the number of rotations (rotations % len(array)), since a number of rotations that is more than the length of the array being rotated can be reduced. I implemented this, but am still getting timeouts.

I suspect that one of my bottlenecks is the fact that I'm creating an empty 2d array of the same number of rows and columns before adding my new rows of data to it, so one possible solution would be for me to figure out how to add the data directly to the given matrix, rather than creating a new empty matrix first.

Current (non-optimized) solution:

def matrixRotation(matrix, r):
    # Number of rows in the matrix
    rows = len(matrix)
    # Number of columns in the matrix
    columns = len(matrix[0])

    # New list that will contain each slice of the matrix in a new list
    matrices = []
    # The number of slices in the matrix
    matrix_num = int(min(rows, columns) / 2)
    i = 0
    while i < matrix_num:
        single_matrix = []
        # The top (left to right) of the matrix slice
        top = matrix[i][i:columns-i]
        # The right side (top to bottom) of the matrix slice
        right = [matrix[row][columns-1-i] for row in range(i+1, rows-i-1)]
        # The bottom (right to left) of the matrix slice
        bottom = matrix[rows-i-1][i:columns-i]
        bottom.reverse()
        # The left side (bottom to top) of the matrix slice
        left = [matrix[row][i] for row in range(i+1, rows-i-1)]
        left.reverse()

        i += 1

        # Append each side of the matrix slice to the single_matrix list, with each side in its own list
        single_matrix.extend([top, right, bottom, left])
        # Turn the single_matrix list of lists into a single list
        single_matrix = [x for x in single_matrix if x != []]
        # Turn into a flat list
        single_matrix = list(itertools.chain(*single_matrix))
        # Rotate this flat list counter clockwise by r
        # If r is more than the length of the matrix it's more efficient to use r % len
        new_r = r % len(single_matrix)
        for _ in range(new_r):
            single_matrix.append(single_matrix.pop(0))
        # Append the rotated list to the matrices list
        matrices.append(single_matrix)

    flat_matrices = list(itertools.chain(*matrices))
    new_matrix = [[] for i in range(rows)]
    i = 0
    rs = rows
    cs = columns
    # Create new matrix from rotated matrix
    while i <= matrix_num:
        # Create new top
        new_matrix[i][i:i] = flat_matrices[:cs]
        del flat_matrices[:cs]
        # Create new right side
        for k in range(rs - 2):
            new_matrix[k + 1 + i].insert(i, flat_matrices[k])
        del flat_matrices[:rs - 2]
        # Create new bottom
        new_matrix[rows - 1 - i][i:i] = flat_matrices[:cs][::-1]
        del flat_matrices[:cs]
        # Create new left side
        for k in range(rs - 2):
            new_matrix[rs - 2 - k + i].insert(i, flat_matrices[k])
        del flat_matrices[:rs - 2]
        i += 1
        rs -= 2
        cs -= 2

    # Print each row as a string joined by a space
    for row in new_matrix:
        print(' '.join(str(x) for x in row))

Here is the problem I'm working on, if anybody is interested:
https://www.hackerrank.com/challenges/matrix-rotation-algo/problem

Hopefully I will have a Part 3 with an optimized solution soon.


0 Comments