티스토리 뷰

통계페이지 만들기

내겐 강의평가 데이터가 있다.

강의평가 데이터로 통계페이지를 만들어볼까 한다.

 

원하는 건, 항목별 가장 높은 점수를 받은 과목이나 교수를 표에 보여주는 것이다.

 

딱히 난이도가 어려운 건 없다.

데이터를 디테일하게 만지는 데에 시간이 좀 오래 걸릴 뿐이다.

 

개요

1. 항목별 평균 구하기

2. 순위 매겨서 Rank model에 저장

3. html 작성

4. ajax 이용하여 항목별 Rank 출력

 

1. 항목별 평균 구하기

Course 아래 각 강의평가가 Response라는 이름으로 foreignkey가 붙어있다. (Response -> Course)

항목은 Question이다, 즉 여러 질문에 대한 5점 척도 평가들이 Response에 들어있다. (Question -> Response)

Question에 대한 답은 Answer에 저장되어 있다.

그러므로 Question에 Answer가 달려있다. (Answer -> Question & Answer -> Response)

 

Course에서 Question의 평균을 구해야 하므로, Response를 거쳐야한다.

Response가 3개 이상인 Course만 통계에 포함시킬 것이므로, 타겟 course는

from django.db.models import Count
from .models import Course, Question, Response

courses = Course.objects.annotate(num_resp=Count('response')).filter(num_resp__gte=3)

annotate는 queryset에서 변수를 설정하는 것이다.

위에선 response의 개수는 num_resp로 정의하고, 그게 3 이상인 course만 빼냈다.

참고한 stackoverflow.

 

비슷한 걸로는 aggregate가 있는데, 잘 알면 유용하게 쓰일 것 같다.

aggregate는 계산한 값이 바로 나오는데, 이걸로도 잘 조합하면 원하는 걸 구할 수는 있을 것 같다.

다만, 나는 한 단계를 더 거쳐야 하고 question에 따라 다른 값이 나와서 annotate를 사용했다.

 

참고로 3개 이상이 아니라 1개 이상이면 __isnull을 사용할 수도 있다.

courses = Course.objects.annotate(num_resp=Count('response')).filter(num_resp__isnull=False)

다만 이건 결과값이 다를 수 있으므로 주의한다.

course가 중복되기 때문이다.

sql로 바꾸면 쿼리가 좀 다른 것 같다.

여튼 그냥 gte=1 쓰자.

 

그 후 question q의 답인 answer의 값을 평균내야 한다.

여기서 좀 헷갈리는데, courses에 각 response의 특정 question에서 answer들의 평균을 내야한다.

import numpy as np
for course in courses:
	resp_set = course.response_set.all()
    np.mean(list(map(lambda x: int(x.answer_set.get(question=question).text), resp_set)))

복잡하다.

내 모델이 복잡하다.

 

2. 순위 매겨서 Rank model에 저장

Rank model은 항목(question)만 있으면 된다.

from django.db import models
class Rank(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    rank_str = models.TextField()
    create_time = models.DateTimeField('time created', auto_now_add=True)
    update_time = models.DateTimeField(auto_now=True)

question에 대해 순위들이 rank_str에 json.dumps로 변환되어 저장된다.

list가 dumps로 씌워졌다.

pickle의 dumps도 좋지만, 내가 admin에서 쉽게 바꾸기 위해 그냥 json을 이용했다. (pickle은 binary로 저장해서 사람이 읽을 수 없다)

(pickle도 admin에서 deserialize 해서 볼 수도 있지만 귀찮)

 

여기서 get_or_create를 썼는데, queryset를 get할 때, 있으면 있는 걸 쓰고, 없으면 만드는 거다.

음... 딱히 중요하지 않으니 넘어가자

 

3. html 작성

디자인은 센스가 필요하다.

나는 그냥 bootstrap에 table을 가져다 썼다.

<table class="table table-condensed">
  ...
</table>

그 위에 네비게이션 바를 넣어서, Question를 누르면 그에 맞는 Rank가 뜨도록 만들었다.

네비게이션 바도 부트스트랩에서 썼다.

 

4. ajax 이용하여 항목별 Rank 출력

요게 귀찮지만 요샌 필수적인 것이다.

이전에 새로고침 없게 데이터를 불러오는 걸 쓴 적이 있다.

똑같다.

 

네비게이션 바를 클릭하면 django view에 데이터를 요청하고, html 형식으로 데이터를 다시 전송해주면 javascript가 그걸 table에 적용해주는 식이다.

 

...
      <ul class="nav nav-tabs">
        {% for q in question %}
            <li role="presentation">
              <a class="nav_tab_elem" question={{ q.id }}>
                {{ q.ask }}
              </a>
            </li>
        {% endfor %}
      </ul>
      
      <table id="table">
      ...
      </table>
...


<script type="text/javascript">
  $(".nav_tab_elem").click(function(){
    var question = $(this).attr('question')
    $.ajax({
      type: "POST", // 데이터를 전송하는 방법을 지정
      url: "{% url 'app_name' %}", // 통신할 url을 지정
      data: {"question": question,'csrfmiddlewaretoken': '{{ csrf_token }}'}, // 서버로 데이터 전송시 옵션
      dataType: "text", // 서버측에서 전송한 데이터를 어떤 형식의 데이터로서 해석할 것인가를 지정, 없으면 알아서 판단
      // 서버측에서 전송한 Response 데이터 형식
      success: function(response){
          $("#table").html(response);
        }
      }
	});
  });
</script>

nav_tab_elem이란 class를 누르면 (네비게이션 바) question이란 attribute(내가 넣어줌)가 POST 형식으로 app_name url로 넘어간다.

data에 저장된 question이랑 같이.

그럼 그걸 django view가 url을 거쳐서 받겠지? (자세한 설정은 이전 글 참고)

그런 다음에 view에서 정보(response)가 오면 dataType으로 인식(parse)하고 nav_tab의 html을 response로 변경한다.

 

def load_stat(request):
	question = request.POST.get('question')
    ...
	return render(request, 'stat_table.html', context)

question 처럼 받으면 되고, return 처럼 보내주면 된다.

'stat_table.html'이 html에서 response가 된다.

 

table을 id로 가진 요소가 response로 바뀌니까, stat_table.html에는 <table>...</.table>이 들어가면 된다.

 

주의할 점은, stat_table.html에 한글이 있으면 unicode decode error가 난다.

고칠 순 있는데, 난 귀찮아서 걍 다 영어로 바꿈.

 

그리고 자바스크립트에서 dataType이 text가 아니라 json이면 parse error가 난다.

json으로 보낼 땐 json, 아니면 text라고 하자.

 

참고로 디버깅할 때 유용한 팁이다

ajax 에러는 여기에 정리되어 있다.

html, javascript 디버깅은 웹 클라이언트가 잘 해준다.

난 개발서버에서 파이어폭스를 사용하므로 파이어폭스 개발자 도구를 사용했다.

break point를 만들고, 콘솔에서 console.log(...)로 출력할 수 있다.

에러 내용도 볼 수 있으니 유용.

 

끝.

너무 간단히 썼다.

'gistalk 개발' 카테고리의 다른 글

flutter - 1. 설치  (0) 2020.07.27
python 3.8 & Django 3.0 설치  (0) 2020.03.13
[Django] forms와 views에서 invalid data error 출력하기  (0) 2019.04.21
Django tas app 추가 6 : template tags & filters  (0) 2018.07.02
crawling하기  (0) 2018.03.19
댓글