Skip to content

Commit 68267e5

Browse files
committed
nicer UI
1 parent 44d0acd commit 68267e5

File tree

3 files changed

+258
-34
lines changed

3 files changed

+258
-34
lines changed

controllers/lesson0_py_intro.py

+141-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
# -*- coding: utf-8 -*-
2-
"""Web2Py pagal puslapio URL adresą iškviečiama funkcija atitinkamame faile (taip vadinamam "controller"'yje)."""
2+
"""Web2Py pagal puslapio URL iškviečia funkciją atitinkamame faile ("controller"'yje)."""
33

44
from plugin_introspect import tutor, menu
55

66

77
def index( ):
88
return menu()
99

10+
1011
@tutor
11-
def hello():
12+
def hello(): # def - nuo žodžio "define" - aprašo funkciją
1213
"""URL pabaigoje "hello", todėl iškviečiama funkcija "hello()" """
1314
return "Hello" # grąžinamą reikšmę matote prie [ Pavyzdys ]
1415

@@ -29,6 +30,142 @@ def kintamieji():
2930

3031

3132
@tutor
32-
def kintamuju_keitimas_TODO():
33+
def kintamuju_kitimas():
34+
a = 5
35+
a += 3 # padidinam 3
36+
a *= 2 # padvigubinam ###PLACEHOLDER:--> a # padvigubinam
37+
return a
38+
39+
40+
@tutor
41+
def _duomenu_tipai():
42+
a = 5
43+
b = 3.14
44+
c = "hello" + ' world' # tekstas gali būt ir tarp viengubų ir tarp dvigubų kabučių
45+
46+
return type(a), BR(), type(b), BR(), type(c) # ps.: BR() atitinka HTML <br>
47+
48+
49+
@tutor
50+
def duomenu_tipu_konvertavimas():
3351
a = 5
34-
a += 8
52+
b = 3.14
53+
c = "hello"
54+
d = "6.66"
55+
56+
a_txt = str( a )
57+
b_int = int( b )
58+
d_float = float( d )
59+
60+
return repr(a_txt), BR(), repr(b_int), BR(), repr(d_float) # repr - reiškia "represent"
61+
# kaip reikšmė atrodo kode -- tekstas rodomas su kabutėm
62+
@tutor
63+
def datos_tipas():
64+
import datetime
65+
66+
jonines = datetime.date(2017, 6, 23)
67+
68+
return jonines
69+
70+
@tutor
71+
def datos_tipas_siandien():
72+
import datetime
73+
74+
siandien = datetime.date.today()
75+
76+
return siandien
77+
78+
79+
@tutor
80+
def datos_skirtumas():
81+
"""raskite kelinta dabar metų diena"""
82+
import datetime
83+
84+
NM_riba = datetime.date(2016, 12, 31) # metų riba
85+
siandien = datetime.date.today()
86+
87+
return (siandien-NM_riba).days ####PLACEHOLDER:--> return ( ? ).days
88+
89+
90+
@tutor
91+
def data_su_laiku():
92+
import datetime
93+
94+
dabar = datetime.datetime.now()
95+
96+
return dabar
97+
98+
99+
@tutor
100+
def _tekstas():
101+
a = "labas"
102+
b = "rytas"
103+
104+
return a + " " + b
105+
106+
@tutor
107+
def multiline_tekstas():
108+
bla = """trigubos kabutės leidžia
109+
rašyti tekstą per kelias eilutes.
110+
111+
Taip pat jos naudojamos docstring'uose (funkcijos aprašymui/paaiškinimams).
112+
"""
113+
114+
return PRE( bla )
115+
116+
@tutor
117+
def tekstas_plius_skaicius():
118+
"""Tiesiogiai sudėt teksto ir skaičiaus nepavyks - bus klaida"""
119+
a = "labas"
120+
b = 10
121+
122+
return a + b
123+
124+
@tutor
125+
def tekstas_info_sujungimas():
126+
name = "Jurgis"
127+
age = 36
128+
129+
return name + " turi " + str(age) +" metus." # str(..) bet ką paverčia tekstu
130+
131+
132+
@tutor
133+
def teksto_formatavimas():
134+
"""Kad nereiktų rašinėt + ir str(..) yra spec. būdai įterpt info į tekstą.
135+
136+
Naudojamas % žymeklis, kuris pažymi, kurioje vietoje info bus įterpta.
137+
o gale po % išvardinamos norimos įterpti reikšmės """
138+
139+
name = "Jurgis"
140+
age = 36
141+
142+
return "%s turi %s metus." % ( name, age )
143+
144+
@tutor
145+
def _duomenu_strukturos():
146+
pass
147+
148+
@tutor
149+
def sarasai():
150+
pass
151+
152+
153+
@tutor
154+
def zodynai():
155+
pass
156+
157+
158+
@tutor
159+
def teksto_formatavimas_su_zodynais():
160+
"""žodyne reikšmes susiejamos pagal pavadinimą/vardą. Analogiškai kaip kintamieji.
161+
162+
Tada vietoj %s nurodoma išsamesnė info: %(pavad)s , kur "pavad" yra reikšmės pavadinimas.
163+
Patogu, nes pasikeitus išsidėstymui tekste, nekils nesusipratimų.
164+
165+
O gale pateikiamas žodynas. Čia panaudojam spec. funkciją locals(),
166+
kuri leidžia paimti visus funkcijos kintamuosius kaip žodyną """
167+
168+
name = "Jurgis"
169+
age = 36
170+
171+
return "%(name)s turi %(age)s metus. %(name)s kartais vėluoja..." % locals()

modules/plugin_introspect.py

+96-23
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,29 @@ def get_controller_code( c=None, app=None ):
2626

2727
return code
2828

29-
def get_exposed_function_code(fname, code=None):
29+
exposed_functions = {} # for singleton
30+
31+
def generate_exposed_functions_info():
32+
33+
34+
global exposed_functions
35+
exposed_functions = getattr(current, 'exposed_functions', {} )
36+
current.exposed_functions = exposed_functions
37+
if not exposed_functions:
38+
for f in exposed_functions_names():
39+
d = exposed_functions[f] = {}
40+
d['code'] = code = get_exposed_function_code( f )
41+
d['is_task'] = '_task' in f or f.startswith('task') or "###PLACEHOLDER" in code
42+
return exposed_functions
43+
44+
def get_exposed_function_code(fun_name, code=None):
45+
3046
if code is None:
3147
code = get_controller_code()
3248

3349
regex_myfun_header = re.compile(
3450
# r'^def\s+(?P<name>_?[a-zA-Z0-9]\w*)\( *\)\s*:',
35-
r'^def\s+(%s)\( *\)\s*:' % fname,
51+
r'^def\s+(%s)\( *\)\s*:' % fun_name,
3652
flags=re.M)
3753
match = regex_myfun_header.search(code)
3854
myfun_header = match and match.group(0)
@@ -64,14 +80,14 @@ def lessons_menu(return_plain=False):
6480
controllers = [ c[:-len('.py')] for c in os.listdir( dirpath ) if c.startswith('lesson') and c.endswith('.py')]
6581
controllers.sort()
6682

67-
menu = [ A( c[len("lesson"):].title(), _href=URL(c, 'index') ) for c in controllers ]
83+
menu = [ A( c[len("lesson"):].title(), _href=URL(c, 'index') ) for c in controllers ]
6884

6985
if return_plain:
7086
return controllers
7187
else:
7288
return UL(menu)
7389

74-
def exposed_functions( ):
90+
def exposed_functions_names():
7591

7692
request = current.request
7793

@@ -83,20 +99,52 @@ def exposed_functions( ):
8399
items = find_exposed_functions(data)
84100

85101
return items
86-
87-
def menu( ):
102+
103+
104+
105+
def menu( only_category = False ):
88106
"""gives links to all exposed functions except currently used"""
89-
items = exposed_functions( )
107+
fun_names = exposed_functions_names()
108+
109+
generate_exposed_functions_info()
90110
request = current.request
91-
return UL( [
92-
item!=request.function
93-
and SPAN( A( item , _href=URL(item)), "*"*is_task(item) )
94-
or item
95-
for item in items
96-
] )
97-
98-
def is_task(fname):
99-
return '_task' in fname or fname.startswith('task')
111+
112+
htmlized = [
113+
item if item==request.function
114+
else SPAN( A( item , _href=URL(item)), "*"*exposed_functions[item]['is_task'] )
115+
116+
for item in fun_names
117+
if item != 'index'
118+
]
119+
120+
ctx = {'current_cat':None}
121+
def transform_to_tree():
122+
"group menu items into categories -- if item name starts with _ it means new category"
123+
result = []
124+
cat = []
125+
cat_name = ""
126+
127+
for name, html in zip( fun_names, htmlized):
128+
if name.startswith('_'): # means category
129+
result .append ( TOGGLABLE_CONTENT( cat_name, UL(cat)) ) #
130+
cat_name = name.replace("_", " ").title()
131+
cat = [ ]
132+
133+
if only_category and name==request.function:
134+
return cat_name # dirty hack to get current category name
135+
136+
cat.append( html )
137+
138+
result.append(TOGGLABLE_CONTENT(cat_name, UL(cat))) # last category
139+
140+
return result
141+
142+
if only_category:
143+
return transform_to_tree()
144+
145+
return UL( transform_to_tree() )
146+
# return UL( htmlized )
147+
100148

101149

102150
def TOGGLABLE_CONTENT(name, content):
@@ -106,11 +154,11 @@ def TOGGLABLE_CONTENT(name, content):
106154
"""
107155

108156
return CAT(
109-
BR(),
157+
110158
SPAN( name, _class="button", _onclick=js_toggle, _style="cursor:hand"),
111159
SPAN( content , _class="togglable")
112160
# SPAN( content, _class="togglable", _style="display:none" )
113-
, BR(), SPAN("", _class="after_togglable")
161+
,SPAN("", _class="after_togglable")
114162
)
115163

116164
def CODEMIRROR(code, language="python", task_key=None):
@@ -121,11 +169,29 @@ def CODEMIRROR(code, language="python", task_key=None):
121169
return XML(current.response.render('codemirror.html', dict(code=code, language=language, task_key=task_key)))
122170

123171

124-
172+
import traceback
125173
def tutor(f):
126174
"""smart decorator"""
127175
def result():
128-
content = f()
176+
try:
177+
content = f()
178+
except Exception as e:
179+
# content = repr( e )
180+
tb_str = traceback.format_exc()
181+
lines = tb_str.split("\n")
182+
# hide path and tutor decorator info
183+
for nr, line in enumerate(lines):
184+
if 'File "' in line and '/controllers/' in line:
185+
a, b = line.split( 'File "', 1)
186+
_, b = b.split('/controllers/', 1)
187+
lines[nr] = a + 'File "'+b
188+
189+
tb_str = "\n".join( lines[:1]+ lines[3:] ) # hide lines about plugin_introspect
190+
content = CAT( "KLAIDA:", PRE( tb_str, _style="color:brown" ) )
191+
192+
if 'plain' in current.request.vars:
193+
return content
194+
129195
about = get_module_doc( get_controller_code() )
130196

131197
codes = TOGGLABLE_CONTENT("[ Kodas ]", get_task_code(f))
@@ -135,18 +201,25 @@ def result():
135201
menu_ = TOGGLABLE_CONTENT("[ Meniu ]", menu())
136202

137203
# next menu
138-
items = exposed_functions( )
204+
items = exposed_functions_names()
139205
req = current.request
140206
nr = items.index( req.function )
141207
next = items[nr+1] if nr < len(items)-1 else None
142208
prev = items[nr-1] if nr > 0 else None
143209

144210
a_next = A("[ Pirmyn ]", _href=URL(next)) if next!=None else ""
145211
a_prev = A("[ Atgal ]", _href=URL(prev)) if prev!=None else ""
146-
menu_ = CAT( BR(), a_prev, a_next, BR(), menu_ )
212+
navigate_prev_next = CAT( a_prev, " ", a_next )
213+
# menu_ = CAT( BR(), a_prev, a_next, BR(), menu_ )
214+
current_category = menu(only_category=True)
147215

148216

149-
return XML(current.response.render('tutor.html', dict( about=about, content=content, codes=codes, menu=menu_) ) )
217+
return XML(current.response.render('tutor.html',
218+
dict( about=about, content=content, codes=codes, menu=menu_,
219+
navigate_prev_next=navigate_prev_next,
220+
current_category=current_category
221+
)
222+
) )
150223
# return gluon.template.render(content='...', context=<vars>)
151224
return result
152225

0 commit comments

Comments
 (0)