2011년 9월 29일 목요일

replyingtv - index.py

이 프로그램의 메인인 모듈인 index.py를 살펴보겠습니다.

# -*- coding: utf-8 -*-
'''
Created on 2011. 9. 14.

@author: Taejun Park

'''

from datetime import datetime, timedelta
import os
import logging

from google.appengine.api import memcache
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext import db
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext.db import djangoforms

#I made
import model
import getPrograms

#not yet attached
from django.utils import simplejson as json
 

def userLogging(self):
    """
    Args:
    self: page handler
    Returns:
    Logout(in) URL as a string, 'Login(out)'
    """
    if users.get_current_user():                                                        
        return users.create_logout_url(self.request.uri), 'Logout'
    else:
        return users.create_login_url(self.request.uri), 'Login'
    

def dateChanged(now):
    """
    Args:
    now : Datetime class
    Returns:
    True if changed, False if not    
    it checks wheather correct date was saved in the memcache/Parameter
    after it works, can use correct date guide
    """       
    
    today = now.date()
    
    programGuideDate = memcache.get("programGuideDate")
    
    #if memcache is empty
    if programGuideDate is None:
        parameter = model.Parameter.get_by_key_name(key_names='day')
        
        if parameter is None:   #if it is the first run
            model.Parameter(key_name='day', date=today ).put()
            if not memcache.add("programGuideDate", today): #save into memcache
                logging.error("Memcache set failed")
            return True
    
        programGuideDate = parameter.date
        
        if not memcache.add("programGuideDate", programGuideDate):  
            logging.error("Memcache set failed")
            
          
    if today > programGuideDate:    #if date changed
        #if now is after 4AM or more than 2days after
        if now.hour > 4 or today-programGuideDate > timedelta(1): 
            model.Parameter(key_name='day', date=today).put()
            if not memcache.add("programGuideDate", today):
                logging.error("Memcache set failed")
            return True
    #if not new day or tomorrow before 4am
    return False


def getBroadcastingProgramNow(broadcastingChannel):
    """
    Arg:
    broadcastingChannel : only KBS1 or KBS2 is supported now
    Return:
    ProgramGuide instance
    in this method, programLists: [program title+datetime] lists
    program = title+datetime+channel
    """    
    now = datetime.now()
    if dateChanged(now):  #if date changed, delete yesterday's program guide db with the same channel
        db.delete(model.ProgramGuide.gql("WHERE channel = :1", broadcastingChannel))
        
        #getPrograms returns [programtitle,datetime] as a list
        programLists = getPrograms.getPrograms(broadcastingChannel) #programs is list
        
        #parse programLists. the program name was got from KBS hompage so the encoding is euckr
        for program in programLists:
            model.ProgramGuide(key_name=program[0].decode('euckr', 'ignore'), programTime=program[1], channel=broadcastingChannel).put()
        
        #get if from db to match the type the same
        programs = model.ProgramGuide.gql("WHERE channel = :1 ORDER BY programTime ASC", broadcastingChannel)
        
        if not memcache.add(broadcastingChannel, programs):
            logging.error("Memcache set failed")
            
    else:   #check if the time pasts
        programs = memcache.get(broadcastingChannel)
        
        if programs is None:    #if memcache is empty, get it from db, add it to the memcache
            programs = model.ProgramGuide.gql("WHERE channel = :1 ORDER BY programTime ASC", broadcastingChannel)
            if not memcache.add(broadcastingChannel, programs):
                logging.error("Memcache set failed")
    
    #for ordered for clause. for more information, visit:
    #http://code.google.com/intl/ko-KR/appengine/docs/python/datastore/queries.html#Query_Cursors
    start_cursor = memcache.get("program_start_cursor")
    end_cursor = memcache.get("program_end_cursor")
    if start_cursor:
        programs.with_cursor(start_cursor = start_cursor)
    if end_cursor:
        programs.with_cursor(end_cursor = end_cursor)
    
    for program in programs:
        if program.programTime > now :  #if there's no program matches, it means there's no program now so return "화면조정"
            if not memcache.add("program_start_cursor", start_cursor):
                logging.error("Memcache set failed")    
            return program
    return None


class RecentCommentsPage(webapp.RequestHandler):
    """this class get 30 comments from db and match it for comments.html
    """
    def get(self):
        url, url_linktext = userLogging(self)
        comments = model.Comment.all().order('-written_datetime').fetch(30)
                    
        username = users.get_current_user()
                
        template_values={
                         'username':username,
                         'url':url,
                         'url_linktext':url_linktext,
                         'comments':comments,
                         }
        path=os.path.join(os.path.dirname(__file__), 'comments.html')
        self.response.out.write(template.render(path, template_values))
        
class MainPage(webapp.RequestHandler):
    """get which channel user want.
    then get what program is running now. get comments for the program
    send them to index.html
    """
    def get(self):
        #because there are not enough webpages, this program can only run over the KBSs.
        if self.request.path == '/2tv':
            channel = 'KBS2'
        else:
            channel = 'KBS1'    
        
        #get what program is running. probramNow programGuide Instance   
        programNow=getBroadcastingProgramNow(channel)
        
        if programNow is not None:        
            programInstance = model.Program.get_or_insert(key_name=programNow.key().name(), channel=channel)
            comments = model.Comment.gql("WHERE program = :program ORDER BY written_datetime DESC", program=programInstance).fetch(5)
            program = programNow.key().name()
        else:
            comments = None        
            program = None
        
        #make user signing up url
        url, url_linktext = userLogging(self)
        
        #HTML forms for POST
        form = ApplyingForm()
        
        username = users.get_current_user()

        template_values={
                         'channel':channel,
                         'head_title': '프로그램 목록',
                         'page_title': 'TV 프로그램 댓글',
                         'program': program,
                         'username': username,                                                
                         'url':url,
                         'url_linktext':url_linktext,   
                         'form': form,
                         'comments': comments                           
                         }
        path=os.path.join(os.path.dirname(__file__), 'index.html')
        self.response.out.write(template.render(path, template_values))
        
    #see http://code.google.com/intl/ko-KR/appengine/articles/djangoforms.html
    def post(self):
        #get POST requests
        data = ApplyingForm(data=self.request.POST)
        
        if data.is_valid():
            entity=data.save(commit=False)

            #get commenter: now this program saves email address of current user
            entity.commenter = users.get_current_user().email()
            
            entity.written_datetime =datetime.now()
            
            entity.written_via = 'replyingTV'  #later would be another platform
            
            #get program name from the web page, and get the model reference matches
            #first from the memcache, sencond from the db
            programNow = self.request.get('program')
            programInstance = memcache.get(programNow)
            if not programInstance:
                programInstance = model.Program.get_by_key_name(programNow)
            entity.program = programInstance
            
            try:         
                entity.put()    #save
            
            except :
                print "don't type that long, plz less than 500 bytes"
            finally:
            #redirect it to 2tv if it was from 2tv
                if self.request.get('channel') == 'KBS2':
                    self.redirect('/2tv', permenent=True)           
            
                self.redirect('')
            
        else:
            #redirect it to 2tv if it was from 2tv
            if self.request.get('channel') == 'KBS2':
                self.redirect('/2tv', permenent=True)           
        
            self.redirect('')
class ApplyingForm(djangoforms.ModelForm):
    """this class makes form for the Comment model
    see http://code.google.com/intl/ko-KR/appengine/articles/djangoforms.html
    """
    class Meta:
        model = model.Comment
        exclude=['commenter', 'written_datetime', 'program', 'written_via']
    
class TextPage(webapp.RequestHandler):
    """link to the replyingtv-program guide page
    """
    def get(self):
        self.redirect("http://tj850413.blogspot.com/", permanent=True)
        
def main():
    application = webapp.WSGIApplication([
                                          ('/about', TextPage),
                                          ('/2tv', MainPage),
                                          ('/comment', RecentCommentsPage),
                                          ('/.*', MainPage),
                                          ], debug=True)
    run_wsgi_app(application)
    
    
if __name__ == '__main__':
    main()

시간에 쫓겨 만들다보니 생각하던 것 보단 지저분한 코드가 나왔는데요, 하나하나 살펴보겠습니다.

def userLogging(self) - 앱엔진에서 제공하는 구글 id 로그인 모듈입니다. self클래스를 받아 로그인url과 문자열 login/logout을 리턴합니다. url은 나중에 index.html에서 로그인 url로써 쓰이고, 문자열login/logout은 역시 index.html에서 링크를 나타낼 때 쓰일 문자열입니다.

def dateChanged(now) - datetime인 now를 입력받아 편성표의 날짜가 바뀌었으면 True, 아니면 False를 리턴합니다.

def getBroadcastingProgramNow(broadcastingChannel) - broadCastingChannel을 입력받아 현재 채널의 방송중인 프로그램을 찾아  ProgramGuide 인스턴스를 리턴해줍니다.

class RecentCommentsPage(webapp.RequestHandler) - /comment 페이지의get메시지를 처리해줍니다. 코멘트를 위한 gql query를 보낸 후 최근의 30개만 보여줍니다.


class MainPage(webapp.RequestHandler) - 루트 페이지로 들어오는 get 및 post를 처리해줍니다. get 함수에선 Program 인스턴스를 등록해줍니다. post함수에선 html을 통해 들어온 새로운 comment를 등록해줍니다.


class ApplyingForm(djangoforms.ModelForm) - 장고의 모델폼을 상속하여, 폼을 등록하는데 사용하는 클래스입니다.


class TextPage(webapp.RequestHandler) - 이 블로그로 redirect해주는 클래스입니다.


def main() - WSGIApplication으로 각각의 페이지들을 유도해줍니다.

댓글 없음:

댓글 쓰기

function foo()
{
}