11#!/usr/bin/env python3
2+
23import logging
34import os
45import datetime
1314salt = os .getenv ('DCOLEMAN_TASKS_VIEWER_HASH_SALT' , '1two3' )
1415title = "Django Coleman - Task Viewer | {}" .format
1516mtasks_url = (endpoint + "/tasks/{}/" ).format
17+ port = int (os .getenv ('PORT' , 8888 ))
1618master_t = os .getenv ('DCOLEMAN_MASTER_TOKEN' , 'porgs' ) # A master token to access to any order,
1719 # REPLACE in production or leave it blank to disable
1820
21+ logger = logging .getLogger ('dcoleman-viewer' )
22+ logger .setLevel (logging .INFO )
23+ handler = logging .StreamHandler ()
24+ handler .setFormatter (logging .Formatter ('%(asctime)s %(levelname)s [%(name)s] %(message)s' ))
25+ logging .root .addHandler (handler )
26+ logging .root .setLevel (logging .INFO )
27+
28+
29+ def get_assigned_to (user ):
30+ if not user :
31+ return ""
32+ full_name = " " .join (
33+ filter (None , (user .get ("first_name" ), user .get ("last_name" )))
34+ )
35+ return full_name if full_name else user ["username" ]
36+
37+
38+ def get_created_at (created_at ):
39+ if not created_at :
40+ return ""
41+ return datetime .datetime .strptime (created_at , "%Y-%m-%dT%H:%M:%S.%fZ" ) \
42+ .strftime ("%B %d, %Y" )
43+
44+
45+ def get_deadline (deadline ):
46+ if not deadline :
47+ return ""
48+ return datetime .datetime .strptime (deadline , "%Y-%m-%d" ) \
49+ .strftime ("%B %d, %Y" )
50+
51+
52+ def is_valid_token (order_number , token_url ):
53+ """
54+ Verifies whether the token is valid or not.
55+ It uses the same algorithm used by Django Coleman to
56+ generates the token using as input a salt code and
57+ the Order Number
58+
59+ See: ``mtasks.models.Task#get_tasks_viewer_url`` from the
60+ Django Coleman project
61+ """
62+ if master_t and token_url == master_t : # Master Token is Magic !
63+ return True
64+ pk = int (order_number ) # Removes the leading zeros
65+ token = "{}-{}" .format (salt , pk )
66+ token = sha1 (token .encode ('utf-8' )).hexdigest ()
67+ return token == token_url
68+
69+
1970class BaseHandler (RequestHandler ):
71+
2072 def error (self , status , msg = "Unexpected Error" , exc_info = None ):
2173 if exc_info :
22- logging .error ("Unexpected error %s" , exc_info .__class__ .__name__ , exc_info = exc_info )
74+ logger .error ("Unexpected error %s" , exc_info .__class__ .__name__ , exc_info = exc_info )
2375 self .set_status (status )
2476 return self .render ("error.html" , title = title (msg ), error = msg )
2577
2678
2779class MainHandler (BaseHandler ):
80+
2881 async def get (self , task_number ):
2982 token = self .get_argument ("t" , None )
3083 if not token :
3184 return self .error (401 , "Unauthorized Access" )
32- if not self . is_valid_token (task_number , token ):
85+ if not is_valid_token (task_number , token ):
3386 return self .error (401 , "Invalid Authorization Code" )
3487 http_client = AsyncHTTPClient ()
3588 try :
@@ -43,9 +96,9 @@ async def get(self, task_number):
4396 return self .error (500 , exc_info = e )
4497 order = json_decode (response .body )
4598 state = order ['state' ]
46- assigned_to = self . get_assigned_to (order .get ('user' ))
47- created_at = self . get_created_at (order .get ('created_at' ))
48- deadline = self . get_deadline (order .get ('deadline' ))
99+ assigned_to = get_assigned_to (order .get ('user' ))
100+ created_at = get_created_at (order .get ('created_at' ))
101+ deadline = get_deadline (order .get ('deadline' ))
49102 return self .render ("index.html" ,
50103 title = title (f"Task #{ task_number } " ),
51104 order = order ,
@@ -54,45 +107,9 @@ async def get(self, task_number):
54107 state = state ,
55108 assigned_to = assigned_to )
56109
57- def get_assigned_to (self , user ):
58- if not user :
59- return ""
60- full_name = " " .join (
61- filter (None , (user .get ("first_name" ), user .get ("last_name" )))
62- )
63- return full_name if full_name else user ["username" ]
64-
65- def get_created_at (self , created_at ):
66- if not created_at :
67- return ""
68- return datetime .datetime .strptime (created_at , "%Y-%m-%dT%H:%M:%S.%fZ" ) \
69- .strftime ("%B %d, %Y" )
70-
71- def get_deadline (self , deadline ):
72- if not deadline :
73- return ""
74- return datetime .datetime .strptime (deadline , "%Y-%m-%d" ) \
75- .strftime ("%B %d, %Y" )
76-
77- def is_valid_token (self , order_number , token_url ):
78- """
79- Verifies whether the token is valid or not.
80- It uses the same algorithm used by Django Coleman to
81- generates the token using as input a salt code and
82- the Order Number
83-
84- See: ``mtasks.models.Task#get_tasks_viewer_url`` from the
85- Django Coleman project
86- """
87- if master_t and token_url == master_t : # Master Token is Magic !
88- return True
89- pk = int (order_number ) # Removes the leading zeros
90- token = "{}-{}" .format (salt , pk )
91- token = sha1 (token .encode ('utf-8' )).hexdigest ()
92- return token == token_url
93-
94110
95111class NotFoundHandler (BaseHandler ):
112+
96113 def prepare (self ): # for all methods
97114 return self .error (404 , "Resource Not Found" )
98115
@@ -104,6 +121,15 @@ def make_app():
104121
105122
106123if __name__ == "__main__" :
124+ logger .info ('Starting server at port %d' , port )
107125 app = make_app ()
108- app .listen (8888 )
109- IOLoop .current ().start ()
126+ app .listen (port )
127+ try :
128+ loop = IOLoop .current ()
129+ loop .start ()
130+ except KeyboardInterrupt :
131+ pass
132+ finally :
133+ loop .stop ()
134+ loop .close (True ) # needed to close all open sockets
135+ logger .info ("Server shut down, exiting..." )
0 commit comments