├── .gitignore ├── LICENSE ├── README.md ├── assignments ├── program10A.py ├── program1A.py ├── program2A.py ├── program3A.py ├── program4A.py ├── program5A.py └── program6A.py ├── build.bat ├── hola.py ├── ide2py.spec ├── ide2py ├── autocompletion.py ├── browser.py ├── camera.py ├── checker.py ├── connector.py ├── console.py ├── database.py ├── debugger.py ├── diff.py ├── diffutil.py ├── editor.py ├── explorer.py ├── fileutil.py ├── gui2py.py ├── gui_designer.py ├── ide2py.ini.dist ├── images.py ├── locutil.py ├── main.py ├── psp.py ├── pyparse.py ├── pytong.py ├── qdb.py ├── rad2py.ico ├── repo.py ├── repo_hg.py ├── repo_w2p.py ├── shell.py ├── simplejsonrpc.py ├── splash.png ├── task.py ├── tester.py ├── web2py.py ├── wiki.py └── wxpydiff.py ├── psp2py ├── ABOUT ├── LICENSE ├── __init__.py ├── controllers │ ├── appadmin.py │ ├── default.py │ ├── estimate.py │ ├── probe.py │ ├── projects.py │ ├── reports.py │ ├── services.py │ ├── tests.py │ ├── webservices.py │ └── wiki.py ├── cron │ └── crontab ├── languages │ ├── es-es.py │ ├── fr-ca.py │ ├── fr-fr.py │ ├── hi-hi.py │ ├── hu-hu.py │ ├── hu.py │ ├── it-it.py │ ├── it.py │ ├── pl-pl.py │ ├── pl.py │ ├── pt-br.py │ ├── pt-pt.py │ ├── pt.py │ ├── ru-ru.py │ ├── sk-sk.py │ └── zh-tw.py ├── models │ ├── db.py │ ├── db_psp.py │ ├── db_wiki.py │ └── menu.py ├── modules │ ├── __init__.py │ ├── draws.py │ ├── integration.py │ └── statistics.py ├── static │ ├── css │ │ ├── base.css │ │ ├── calendar.css │ │ ├── handheld.css │ │ ├── superfish-navbar.css │ │ ├── superfish-vertical.css │ │ └── superfish.css │ ├── favicon.ico │ ├── images │ │ ├── arrows-ffffff.png │ │ ├── css3buttons_backgrounds.png │ │ ├── css3buttons_icons.png │ │ ├── error.png │ │ ├── ok.png │ │ ├── poweredby.png │ │ ├── shadow.png │ │ ├── warn.png │ │ └── warning.png │ ├── js │ │ ├── calendar.js │ │ ├── dd_belatedpng.js │ │ ├── jquery.js │ │ ├── modernizr-1.7.min.js │ │ ├── superfish.js │ │ └── web2py_ajax.js │ └── robots.txt └── views │ ├── __init__.py │ ├── appadmin.html │ ├── default │ ├── index.html │ └── user.html │ ├── estimate │ ├── correlation.html │ ├── index.html │ ├── significance.html │ └── time_in_phase.html │ ├── generic.html │ ├── generic.json │ ├── generic.load │ ├── generic.pdf │ ├── generic.rss │ ├── generic.xml │ ├── layout.html │ ├── probe │ ├── categorize.html │ ├── index.html │ └── library.html │ ├── projects │ ├── create.html │ ├── edit.html │ ├── index.html │ └── show.html │ ├── reports │ ├── defects.html │ ├── index.html │ └── projects.html │ ├── web2py_ajax.html │ └── wiki │ ├── edit.html │ ├── index.html │ ├── load.html │ └── view.html ├── screenshots ├── ide2py-0.05-ubuntu.png ├── ide2py-v0.03-ubuntu.png ├── proof-of-concept.png ├── prototipe-alpha-win7.png ├── psp_average_fix_times.png ├── psp_defect.png ├── psp_defect_report.png ├── psp_defects.png ├── psp_estimate.png ├── psp_linear_regression.png ├── psp_metadata.png ├── psp_normal_distribution.png ├── psp_pareto_distribution.png ├── psp_plan_summary_times.png ├── psp_probe.png ├── psp_project.png ├── psp_report_projects.png ├── psp_reports.png ├── psp_reuse_library.png ├── psp_toolbar.png ├── psp_wiki.png ├── pytong.png └── wxPyDiff.png └── setup_qdb.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rad2py 2 | ====== 3 | 4 | Rapid Aplication Development platform for python (mirror) 5 | -------------------------------------------------------------------------------- /assignments/program10A.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | "PSP Program 6A - Linear Regression Prediction Interval" 5 | 6 | __author__ = "Mariano Reingart (reingart@gmail.com)" 7 | __copyright__ = "Copyright (C) 2011 Mariano Reingart" 8 | __license__ = "GPL 3.0" 9 | 10 | 11 | from math import sqrt, pi 12 | 13 | # reuse previous programs 14 | from program1A import mean 15 | from program5A import simpson_rule_integrate, gamma 16 | 17 | 18 | def double_sided_student_t_probability(t, n): 19 | "Calculate the p-value using a double sided student t distribution" 20 | # create the function for n degrees of freedom: 21 | k = gamma(n + 1, 2) / (sqrt(n * pi) * gamma(n, 2)) 22 | f_t_dist = lambda u: k * (1 + (u ** 2) / float(n)) ** (- (n + 1) / 2.0) 23 | # integrate a finite area from the origin to t 24 | p_aux = simpson_rule_integrate(f_t_dist, 0, t) 25 | # return the area of the two tails of the distribution (symmetrical) 26 | return (0.5 - p_aux) * 2 27 | 28 | 29 | def double_sided_student_t_value(p, n): 30 | "Calculate the t-value using a double sided student t distribution" 31 | # replaces table lookup, thanks to http://statpages.org/pdfs.html 32 | v = dv = 0.5 33 | t = 0 34 | while dv > 0.000001: 35 | t = 1 / v - 1 36 | dv = dv / 2 37 | if double_sided_student_t_probability(t, n) > p: 38 | v = v - dv 39 | else: 40 | v = v + dv 41 | return t 42 | 43 | 44 | def variance(x_values, y_values, b0, b1): 45 | "Calculate the mean square deviation of the linear regeression line" 46 | # take the variance from the regression line instead of the data average 47 | sum_aux = sum([(y - b0 - b1 * x) ** 2 for x, y in zip(x_values, y_values)]) 48 | n = float(len(x_values)) 49 | return (1 / (n - 2.0)) * sum_aux 50 | 51 | 52 | def prediction_interval(x_values, y_values, x_k, alpha): 53 | """Calculate the linear regression parameters for a set of n values 54 | then calculate the upper and lower prediction interval 55 | 56 | """ 57 | 58 | # calculate aux variables 59 | x_avg = mean(x_values) 60 | y_avg = mean(y_values) 61 | n = len(x_values) 62 | sum_xy = sum([(x_values[i] * y_values[i]) for i in range(n)]) 63 | sum_x2 = sum([(x_values[i] ** 2) for i in range(n)]) 64 | 65 | # calculate regression coefficients 66 | b1 = (sum_xy - (n * x_avg * y_avg)) / (sum_x2 - n * (x_avg ** 2)) 67 | b0 = y_avg - b1 * x_avg 68 | 69 | # calculate the t-value for the given alpha p-value 70 | t = double_sided_student_t_value(1 - alpha, n - 2) 71 | 72 | # calculate the standard deviation 73 | sigma = sqrt(variance(x_values, y_values, b0, b1)) 74 | 75 | # calculate the range 76 | sum_xi_xavg = sum([(x - x_avg) ** 2 for x in x_values], 0.0) 77 | aux = 1 + (1 / float(n)) + ((x_k - x_avg) ** 2) / sum_xi_xavg 78 | p_range = t * sigma * sqrt(aux) 79 | 80 | # combine the range with the x_k projection: 81 | return b0, b1, p_range, x_k + p_range, x_k - p_range, t 82 | 83 | 84 | def test_student_t_integration(): 85 | # test student t values 86 | assert round(double_sided_student_t_probability(t=1.8595, n=8), 4) == 0.1 87 | assert round(double_sided_student_t_value(p=0.1, n=8), 4) == 1.8595 88 | 89 | 90 | if __name__ == "__main__": 91 | test_student_t_integration() 92 | # Table D8 "Size Estimating regression data" 93 | est_loc = [130, 650, 99, 150, 128, 302, 95, 945, 368, 961] 94 | act_new_chg_loc = [186, 699, 132, 272, 291, 331, 199, 1890, 788, 1601] 95 | projection = 644.429 96 | # 70 percent 97 | b0, b1, p_range, upi, lpi, t = prediction_interval( 98 | est_loc, act_new_chg_loc, projection, alpha=0.7) 99 | print "70% Prediction interval: ", b0, b1, p_range, upi, lpi, t 100 | assert round(t, 3) == 1.108 101 | assert round(b0, 2) == -22.55 102 | assert round(b1, 4) == 1.7279 103 | assert round(p_range, 3) == 236.563 104 | assert round(upi, 2) == 880.99 105 | assert round(lpi, 2) == 407.87 106 | # 90 percent 107 | b0, b1, p_range, upi, lpi, t = prediction_interval( 108 | est_loc, act_new_chg_loc, projection, alpha=0.9) 109 | print "90% Prediction interval: ", b0, b1, p_range, upi, lpi, t 110 | assert round(t, 2) == 1.86 111 | assert round(p_range, 2) == 396.97 112 | assert round(upi, 2) == 1041.4 113 | assert round(lpi, 2) == 247.46 114 | 115 | -------------------------------------------------------------------------------- /assignments/program1A.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | """PSP Program 1A - Standard Deviation 5 | """ 6 | 7 | __author__ = "Mariano Reingart (reingart@gmail.com)" 8 | __copyright__ = "Copyright (C) 2011 Mariano Reingart" 9 | __license__ = "GPL 3.0" 10 | 11 | import math 12 | 13 | 14 | def mean(values): 15 | """Calculate the average of the numbers given: 16 | 17 | >>> mean([1, 2, 3]) 18 | 2.0 19 | >>> mean([1, 2]) 20 | 1.5 21 | >>> mean([1, 3]) 22 | 2.0 23 | """ 24 | return sum(values) / float(len(values)) 25 | 26 | 27 | def stddev(values): 28 | """Calculate the standard deviation of a list of number values: 29 | 30 | >>> stddev([160, 591, 114, 229, 230, 270, 128, 1657, 624, 1503]) 31 | 572.026844746915 32 | >>> stddev([186, 699, 132, 272, 291, 331, 199, 1890, 788, 1601]) 33 | 625.6339806770231 34 | >>> stddev([15.0, 69.9, 6.5, 22.4, 28.4, 65.9, 19.4, 198.7, 38.8, 138.2]) 35 | 62.25583060601187 36 | """ 37 | x_avg = mean(values) 38 | n = len(values) 39 | return math.sqrt(sum([(x_i - x_avg)**2 40 | for x_i in values]) / float(n - 1)) 41 | 42 | 43 | if __name__ == "__main__": 44 | # Table D5 "Object LOC" column 45 | sd = stddev([160, 591, 114, 229, 230, 270, 128, 1657, 624, 1503]) 46 | assert round(sd, 2) == 572.03 47 | # Table D5 "New and Changed LOC" column 48 | sd = stddev([186, 699, 132, 272, 291, 331, 199, 1890, 788, 1601]) 49 | assert round(sd, 2) == 625.63 50 | # Table D5 "Development Hours" column 51 | sd = stddev([15.0, 69.9, 6.5, 22.4, 28.4, 65.9, 19.4, 198.7, 38.8, 138.2]) 52 | assert round(sd, 2) == 62.26 53 | 54 | -------------------------------------------------------------------------------- /assignments/program2A.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | "PSP Program 2A - Count Program LOC" 5 | 6 | __author__ = "Mariano Reingart (reingart@gmail.com)" 7 | __copyright__ = "Copyright (C) 2011 Mariano Reingart" 8 | __license__ = "GPL 3.0" 9 | 10 | 11 | from tokenize import generate_tokens, NEWLINE, COMMENT, NL 12 | import token 13 | 14 | # WARNING: it seems that tokenize counts BOM as a logical line! 15 | 16 | def count_logical_lines(filename): 17 | """Count logical python lines and comments using Tokenizer 18 | 19 | >>> f = open("test1.py", "w") 20 | >>> f.write('#test\n\"\"\"docstring\n\"\"\"\n(1+\n1)\n\n') 21 | >>> f.close() 22 | >>> count, comments = count_logical_lines("test1.py") 23 | >>> count 24 | 2 25 | >>> comments 26 | 1 27 | """ 28 | locs = 0 29 | comments = 0 30 | with open(filename) as f: 31 | g = generate_tokens(f.readline) # tokenize 32 | for toknum, tokval, start, end, line in g: 33 | if toknum == NEWLINE: # count logical line: 34 | locs += 1 35 | if toknum == COMMENT: 36 | comments += 1 37 | return locs, comments 38 | 39 | 40 | def logical_to_physical_count(filename): 41 | """Place every logical line into a pythical line and count physical lines 42 | 43 | >>> f = open("test1.py", "w") 44 | >>> f.write('#test\n\"\"\"docstring\n\"\"\"\n(1+\n1)\n\n') 45 | >>> f.close() 46 | >>> logical_to_physical_count("test1.py") 47 | 2 48 | """ 49 | 50 | # convert physical to logical lines: 51 | with open(filename) as f: 52 | with open(filename + ".phy", "w") as out: 53 | g = generate_tokens(f.readline) # tokenize 54 | prev_toknum = None 55 | last_col = 0 56 | buf = "" 57 | ident = 0 58 | for toknum, tokval, start, end, line in g: 59 | srow, scol = start 60 | erow, ecol = end 61 | if toknum == token.INDENT: # increment identation 62 | ident += 1 63 | elif toknum == token.DEDENT: # decrement identation 64 | ident -= 1 65 | elif toknum == token.STRING and prev_toknum in (token.INDENT, 66 | token.NEWLINE, NL, None): 67 | # Docstring detected, replace by a single line 68 | buf += "'docstring - 1 SLOC'" 69 | elif toknum == COMMENT: 70 | # comment, do nothing 71 | pass 72 | elif toknum == NEWLINE: 73 | if buf: 74 | out.write("%s%s\n" % (" " * ident, buf)) 75 | buf = "" 76 | last_col = 0 77 | elif toknum == NL: 78 | # ignore internal new lines (add space to preserve syntax) 79 | if buf: 80 | buf += " " 81 | elif tokval: 82 | # logical line (docstrig previously printed): 83 | if last_col < scol: 84 | buf += " " 85 | buf += "%s" % tokval 86 | prev_toknum = toknum 87 | last_col = ecol 88 | 89 | # count new physical lines from output file: 90 | count = 0 91 | with open(filename + ".phy") as f: 92 | for line in f: 93 | # fail if it is a comment 94 | assert not line.strip().startswith("#") 95 | # fail if it is blank 96 | assert len(line.strip()) > 0 97 | count += 1 98 | 99 | return count 100 | 101 | 102 | if __name__ == "__main__": 103 | # test program1A 104 | loc1, comments1 = count_logical_lines("program1A.py") 105 | phy_loc1 = logical_to_physical_count("program1A.py") 106 | print loc1, phy_loc1, comments1 107 | assert loc1 == 21 108 | assert comments1 == 5 109 | assert phy_loc1 == loc1 110 | # test program2A 111 | loc2, comments2 = count_logical_lines("program2A.py") 112 | phy_loc2 = logical_to_physical_count("program2A.py") 113 | print loc2, phy_loc2, comments2 114 | assert loc2 == 73 115 | assert comments2 == 18 116 | assert phy_loc2 == loc2 117 | -------------------------------------------------------------------------------- /assignments/program3A.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | "PSP Program 3A - Count LOC per class/method or function" 5 | 6 | __author__ = "Mariano Reingart (reingart@gmail.com)" 7 | __copyright__ = "Copyright (C) 2011 Mariano Reingart" 8 | __license__ = "GPL 3.0" 9 | 10 | 11 | import os.path 12 | import pyclbr 13 | from tokenize import generate_tokens, NEWLINE, COMMENT, INDENT, DEDENT 14 | 15 | # Constants (for results lists) 16 | LINENO = 0 17 | CLASS = 1 18 | FUNCTION = 2 19 | LOC = 3 20 | 21 | # WARNING: it seems that tokenize counts BOM as a logical line! 22 | 23 | 24 | def find_functions_and_classes(modulename, path): 25 | """Parse the file and return [('lineno', 'class name', 'function')] 26 | 27 | >>> with open("test1.py", "w") as f: 28 | ... f.write("def hola():\n pass\n#\ndef chau(): pass\n") 29 | ... f.write("class Test:\n def __init__():\n\n pass\n") 30 | >>> results = find_functions_and_classes("test1", ".") 31 | >>> results 32 | [[1, None, 'hola', 0], [3, None, 'chau', 0], [5, 'Test', '__init__', 0]] 33 | 34 | """ 35 | # Assumptions: there is only one function/class per line (syntax) 36 | # class attributes & decorators are ignored 37 | # imported functions should be ignored 38 | # inheritance clases from other modules is unhandled (super)doctest for results failed, exception NameError("name 'results' is not defined",) 39 | 40 | result = [] 41 | module = pyclbr.readmodule_ex(modulename, path=path and [path]) 42 | for obj in module.values(): 43 | if isinstance(obj, pyclbr.Function) and obj.module == modulename: 44 | # it is a top-level global function (no class) 45 | result.append([obj.lineno, None, obj.name, 0]) 46 | elif isinstance(obj, pyclbr.Class) and obj.module == modulename: 47 | # it is a class, look for the methods: 48 | for method, lineno in obj.methods.items(): 49 | result.append([lineno, method, obj.name, 0]) 50 | # sort using lineno: 51 | result.sort(key=lambda x: x[LINENO]) 52 | return result 53 | 54 | 55 | def get_object(objects, lineno): 56 | "Find the object at the lineno" 57 | obj = None 58 | ret = None 59 | for obj in objects: 60 | if obj[LINENO] > lineno: 61 | break 62 | ret = obj 63 | return ret 64 | 65 | 66 | def count_logical_lines_per_object(filename): 67 | """Count logical python lines and comments using Tokenizer, grouping 68 | line count per object (using find_functions_and_classes) 69 | 70 | >>> with open("test1.py", "w") as f: 71 | ... f.write("def hola():\n pass\n#\ndef chau(): pass\n") 72 | ... f.write("class Test:\n def __init__():\n\n pass\n") 73 | >>> results = count_logical_lines_per_object("test1.py") 74 | >>> results 75 | ([[1, None, 'hola', 1], [3, None, 'chau', 2], [5, 'Test', '__init__', 2]], 6, 1) 76 | """ 77 | modulename = os.path.splitext(os.path.basename(filename))[0] 78 | path = os.path.dirname(filename) 79 | 80 | locs = 0 81 | comments = 0 82 | 83 | # get the objects, they must be sorted by lineno 84 | objects = find_functions_and_classes(modulename, path) 85 | obj = None # current object 86 | indent = 0 # indentation 87 | base_indent = None # current function/class indentation (block detection) 88 | with open(filename) as f: 89 | g = generate_tokens(f.readline) # tokenize 90 | for toknum, tokval, start, end, line in g: 91 | srow, scol = start 92 | erow, ecol = end 93 | if toknum == INDENT: # increment identation 94 | indent += 1 95 | elif toknum == DEDENT: # decrement identation 96 | indent -= 1 97 | if base_indent is not None and indent <= base_indent: 98 | base_indent = None 99 | if toknum == NEWLINE: # count logical line: 100 | # get the next object for the current line 101 | obj = get_object(objects, srow) 102 | # store the indentation level (to detect when block is left) 103 | if obj and obj[LINENO] == srow: 104 | base_indent = indent 105 | # sum the logical line (only if first line of obj reached) 106 | if obj and base_indent is not None: 107 | obj[LOC] += 1 108 | locs += 1 109 | if toknum == COMMENT: 110 | comments += 1 111 | return objects, locs, comments 112 | 113 | 114 | if __name__ == "__main__": 115 | # tests: 116 | res = find_functions_and_classes("program1A", ".") 117 | print res 118 | assert res == [[14, None, 'mean', 0], [27, None, 'stddev', 0]] 119 | res = find_functions_and_classes("program2A", ".") 120 | print res 121 | assert res == [[16, None, 'count_logical_lines', 0], \ 122 | [40, None, 'logical_to_physical_count', 0]] 123 | res = count_logical_lines_per_object("program1A.py") 124 | print res 125 | assert res == ([[14, None, 'mean', 3], [27, None, 'stddev', 5]], 21, 5) 126 | res = count_logical_lines_per_object("program2A.py") 127 | print res 128 | assert res == ([[16, None, 'count_logical_lines', 12], 129 | [40, None, 'logical_to_physical_count', 41]], 73, 18) 130 | 131 | 132 | -------------------------------------------------------------------------------- /assignments/program4A.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | """PSP Program 4A - Linear Regression Parameter 5 | """ 6 | 7 | __author__ = "Mariano Reingart (reingart@gmail.com)" 8 | __copyright__ = "Copyright (C) 2011 Mariano Reingart" 9 | __license__ = "GPL 3.0" 10 | 11 | 12 | def mean(values): 13 | """Calculate the average of the numbers given: 14 | 15 | >>> mean([1, 2, 3]) 16 | 2.0 17 | >>> mean([1, 2]) 18 | 1.5 19 | >>> mean([1, 3]) 20 | 2.0 21 | """ 22 | return sum(values) / float(len(values)) 23 | 24 | 25 | def linear_regression(x_values, y_values): 26 | """Calculate the linear regression parameters for a set of n values 27 | 28 | >>> x = 10.0, 8.0, 13.0, 9.0, 11.0, 14.0, 6.0, 4.0, 12.0, 7.0, 5.0 29 | >>> y = 8.04, 6.95, 7.58, 8.81, 8.33, 9.96, 7.24, 4.26, 10.84, 4.82, 5.68 30 | >>> b0, b1 = linear_regression(x, y) 31 | >>> round(b0, 3) 32 | 3.0 33 | >>> round(b1, 3) 34 | 0.5 35 | >>> x = 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 8.0, 19.0, 8.0, 8.0, 8.0 36 | >>> y = 6.58, 5.76, 7.71, 8.84, 8.47, 7.04, 5.25, 12.50, 5.56, 7.91, 6.89 37 | >>> b0, b1 = linear_regression(x, y) 38 | >>> round(b0, 3) 39 | 3.002 40 | >>> round(b1, 3) 41 | 0.5 42 | 43 | """ 44 | 45 | # calculate aux variables 46 | x_avg = mean(x_values) 47 | y_avg = mean(y_values) 48 | n = len(x_values) 49 | sum_xy = sum([(x_values[i] * y_values[i]) for i in range(n)]) 50 | sum_x2 = sum([(x_values[i] ** 2) for i in range(n)]) 51 | 52 | # calculate regression coefficients 53 | b1 = (sum_xy - (n * x_avg * y_avg)) / (sum_x2 - n * (x_avg ** 2)) 54 | b0 = y_avg - b1 * x_avg 55 | 56 | return (b0, b1) 57 | 58 | 59 | if __name__ == "__main__": 60 | # Table D8 "Size Estimating regression data" 61 | est_loc = [130, 650, 99, 150, 128, 302, 95, 945, 368, 961] 62 | est_new_chg_loc = [163, 765, 141, 166, 137, 355, 136, 1206, 433, 1130] 63 | act_new_chg_loc = [186, 699, 132, 272, 291, 331, 199, 1890, 788, 1601] 64 | # Estimated Object versus Actual New and changed LOC 65 | b0, b1 = linear_regression(est_loc, act_new_chg_loc) 66 | print b0, b1 67 | assert round(b0, 2) == -22.55 68 | assert round(b1, 4) == 1.7279 69 | # Estimated New and Changed LOC versus Actal and Changed LOC 70 | b0, b1 = linear_regression(est_new_chg_loc, act_new_chg_loc) 71 | assert round(b0, 2) == -23.92 72 | assert round(b1, 4) == 1.4310 73 | print b0, b1 74 | -------------------------------------------------------------------------------- /assignments/program5A.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | "PSP Program 5A - Numerical Integration" 5 | 6 | __author__ = "Mariano Reingart (reingart@gmail.com)" 7 | __copyright__ = "Copyright (C) 2011 Mariano Reingart" 8 | __license__ = "GPL 3.0" 9 | 10 | from math import e, pi, sqrt 11 | 12 | 13 | def compute_integral(f, x_low, x_high, w, n): 14 | "Compute the numerical approximation of a definite integral" 15 | # composite simpson rule (w and n are the subintervals width and quantity) 16 | term1 = f(x_low) 17 | term2 = 0 18 | for j in xrange(1, n, 2): 19 | term2 += f(x_low + j * w) 20 | term3 = 0 21 | for j in xrange(2, n, 2): 22 | term3 += f(x_low + j * w) 23 | term4 = f(x_high) 24 | y = w / 3.0 * (term1 + 4 * term2 + 2 * term3 + term4) 25 | 26 | return y 27 | 28 | 29 | def simpson_rule_integrate(f, x_low, x_high, error=0.00001): 30 | "Integrate complex funtins within finite limits" 31 | # 1. identify the upper and lower limits of the numerical integration 32 | # correct integration range for symmetrical normalized functions: 33 | if x_high < 0 and x_low == float("-infinity"): 34 | x_high = abs(x_high) 35 | x_low = 0 36 | p = -0.5 37 | elif x_low == float("-infinity"): 38 | x_low = 0 39 | p = 0.5 40 | elif x_low > 0 and x_high == float("infinity"): 41 | x_high = x_low 42 | x_low = 0 43 | p = -0.5 44 | else: 45 | p = 0 46 | # 2. select an initial number N and old result 47 | n = 20 48 | old_y = 0 49 | while True: 50 | # 3. divide the range to get the segment width 51 | w = (x_high - x_low) / n 52 | # 4. compute the numerical integration approximation 53 | y = compute_integral(f, x_low, x_high, w, n) 54 | # 5. compare with the old result if error is permisible 55 | if abs(y - old_y) <= error: 56 | if p >= 0: 57 | return p + y 58 | else: 59 | return - p - y 60 | old_y = y 61 | # 6. double N 62 | n = 2 * n 63 | # 7. repeat ... 64 | 65 | 66 | def factorial(x, step=1): 67 | "Calculate integer or float factorial (for gamma function, step=2)" 68 | if x > step: 69 | return x * factorial(x - step, step) / step 70 | return x / step 71 | 72 | 73 | def gamma(n, d=2): 74 | "Calculate gamma function value for a fraction (numerator & denominator)" 75 | # WARNING: only tested for d=2 ! 76 | if n % 2 != 0 and d == 2: 77 | return factorial(n - 2, step=2.0) * sqrt(pi) 78 | else: 79 | i = n / d 80 | return factorial(i - 1) 81 | 82 | 83 | def simpson_rule_tests(): 84 | "Calculate the probability values of the normal/t distribution" 85 | inf = float("infinity") 86 | 87 | # normal distribution 88 | f_normal_dist = lambda u: (1 / (2 * pi) ** (0.5)) * e ** ((- u ** 2) / 2.0) 89 | p = simpson_rule_integrate(f_normal_dist, - inf, -1.1) 90 | assert round(p, 4) == 0.1357 91 | p = simpson_rule_integrate(f_normal_dist, - inf, 2.5) 92 | assert round(p, 4) == 0.9938 93 | p = simpson_rule_integrate(f_normal_dist, - inf, 0.2) 94 | assert round(p, 4) == 0.5793 95 | 96 | # student t distribution 97 | n = 9 # degrees of freedom 98 | assert round(gamma(n, 2), 4) == 11.6317 99 | k = gamma(n + 1, 2) / (sqrt(n*pi) * gamma(n, 2)) 100 | f_t_dist = lambda u: k * (1 + (u ** 2) / float(n)) ** (- (n +1) / 2.0) 101 | # WARNING: the Table A17 on [HUMPHREY95] pp.524 seems to be wrong... 102 | assert round(f_t_dist(0), 4) == 0.3880 103 | #assert round(f_t_dist(1), 4) == 0.3874 104 | #assert round(f_t_dist(2), 4) == 0.3854 105 | #assert round(f_t_dist(20), 4) == 0.2065 106 | p = simpson_rule_integrate(f_t_dist, - inf, 1.1) 107 | assert round(p, 4) == 0.8501 108 | 109 | n = 4 # degrees of freedom 110 | k = gamma(n + 1, 2) / (sqrt(n*pi) * gamma(n, 2)) 111 | f_t_dist = lambda u: k * (1 + (u ** 2) / float(n)) ** (- (n +1) / 2.0) 112 | p = simpson_rule_integrate(f_t_dist, - inf, 3.747) 113 | assert round(p, 4) == 0.99 114 | 115 | p = simpson_rule_integrate(f_t_dist, 2.132, inf) 116 | assert round(p, 4) == 0.05 117 | 118 | 119 | if __name__ == "__main__": 120 | simpson_rule_tests() 121 | -------------------------------------------------------------------------------- /assignments/program6A.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | "PSP Program 6A - Linear Regression Prediction Interval" 5 | 6 | __author__ = "Mariano Reingart (reingart@gmail.com)" 7 | __copyright__ = "Copyright (C) 2011 Mariano Reingart" 8 | __license__ = "GPL 3.0" 9 | 10 | 11 | from math import sqrt, pi 12 | 13 | # reuse previous programs 14 | from program1A import mean 15 | from program5A import simpson_rule_integrate, gamma 16 | 17 | 18 | def double_sided_student_t_probability(t, n): 19 | "Calculate the p-value using a double sided student t distribution" 20 | # create the function for n degrees of freedom: 21 | k = gamma(n + 1, 2) / (sqrt(n * pi) * gamma(n, 2)) 22 | f_t_dist = lambda u: k * (1 + (u ** 2) / float(n)) ** (- (n + 1) / 2.0) 23 | # integrate a finite area from the origin to t 24 | p_aux = simpson_rule_integrate(f_t_dist, 0, t) 25 | # return the area of the two tails of the distribution (symmetrical) 26 | return (0.5 - p_aux) * 2 27 | 28 | 29 | def double_sided_student_t_value(p, n): 30 | "Calculate the t-value using a double sided student t distribution" 31 | # replaces table lookup, thanks to http://statpages.org/pdfs.html 32 | v = dv = 0.5 33 | t = 0 34 | while dv > 0.000001: 35 | t = 1 / v - 1 36 | dv = dv / 2 37 | if double_sided_student_t_probability(t, n) > p: 38 | v = v - dv 39 | else: 40 | v = v + dv 41 | return t 42 | 43 | 44 | def variance(x_values, y_values, b0, b1): 45 | "Calculate the mean square deviation of the linear regeression line" 46 | # take the variance from the regression line instead of the data average 47 | sum_aux = sum([(y - b0 - b1 * x) ** 2 for x, y in zip(x_values, y_values)]) 48 | n = float(len(x_values)) 49 | return (1 / (n - 2.0)) * sum_aux 50 | 51 | 52 | def prediction_interval(x_values, y_values, x_k, alpha): 53 | """Calculate the linear regression parameters for a set of n values 54 | then calculate the upper and lower prediction interval 55 | 56 | """ 57 | 58 | # calculate aux variables 59 | x_avg = mean(x_values) 60 | y_avg = mean(y_values) 61 | n = len(x_values) 62 | sum_xy = sum([(x_values[i] * y_values[i]) for i in range(n)]) 63 | sum_x2 = sum([(x_values[i] ** 2) for i in range(n)]) 64 | 65 | # calculate regression coefficients 66 | b1 = (sum_xy - (n * x_avg * y_avg)) / (sum_x2 - n * (x_avg ** 2)) 67 | b0 = y_avg - b1 * x_avg 68 | 69 | # calculate the t-value for the given alpha p-value 70 | t = double_sided_student_t_value(1 - alpha, n - 2) 71 | 72 | # calculate the standard deviation 73 | sigma = sqrt(variance(x_values, y_values, b0, b1)) 74 | 75 | # calculate the range 76 | sum_xi_xavg = sum([(x - x_avg) ** 2 for x in x_values], 0.0) 77 | aux = 1 + (1 / float(n)) + ((x_k - x_avg) ** 2) / sum_xi_xavg 78 | p_range = t * sigma * sqrt(aux) 79 | 80 | # combine the range with the x_k projection: 81 | return b0, b1, p_range, x_k + p_range, x_k - p_range, t 82 | 83 | 84 | def test_student_t_integration(): 85 | # test student t values 86 | assert round(double_sided_student_t_probability(t=1.8595, n=8), 4) == 0.1 87 | assert round(double_sided_student_t_value(p=0.1, n=8), 4) == 1.8595 88 | 89 | 90 | if __name__ == "__main__": 91 | test_student_t_integration() 92 | # Table D8 "Size Estimating regression data" 93 | est_loc = [130, 650, 99, 150, 128, 302, 95, 945, 368, 961] 94 | act_new_chg_loc = [186, 699, 132, 272, 291, 331, 199, 1890, 788, 1601] 95 | projection = 644.429 96 | # 70 percent 97 | b0, b1, p_range, upi, lpi, t = prediction_interval( 98 | est_loc, act_new_chg_loc, projection, alpha=0.7) 99 | print "70% Prediction interval: ", b0, b1, p_range, upi, lpi, t 100 | assert round(t, 3) == 1.108 101 | assert round(b0, 2) == -22.55 102 | assert round(b1, 4) == 1.7279 103 | assert round(p_range, 3) == 236.563 104 | assert round(upi, 2) == 880.99 105 | assert round(lpi, 2) == 407.87 106 | # 90 percent 107 | b0, b1, p_range, upi, lpi, t = prediction_interval( 108 | est_loc, act_new_chg_loc, projection, alpha=0.9) 109 | print "90% Prediction interval: ", b0, b1, p_range, upi, lpi, t 110 | assert round(t, 2) == 1.86 111 | assert round(p_range, 2) == 396.97 112 | assert round(upi, 2) == 1041.4 113 | assert round(lpi, 2) == 247.46 114 | 115 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | rem Build dist folder using pyinstaller 3 | 4 | python pyinstaller-1.5.1/Build.py ide2py.spec 5 | -------------------------------------------------------------------------------- /hola.py: -------------------------------------------------------------------------------- 1 | # sample file to test shell & debugger 2 | 3 | a = 1 4 | print("hola mundo!") 5 | exit() 6 | a = 1/0 7 | a = 1/3 8 | 9 | def factorial(n): 10 | """This is the "factorial" function 11 | >>> factorial(5) 12 | 120 13 | 14 | """ 15 | if n is None: 16 | raise RuntimeError("test exception") 17 | result = 1 18 | factor = 2 19 | while factor <= n: 20 | result *= factor 21 | factor += 1 22 | return result 23 | 24 | # funcion principal: 25 | 26 | def main(h="CHAU!"): 27 | 28 | a = h 29 | n = raw_input("nombre?") 30 | 31 | n = n+"ldasf" 32 | 33 | if n: 34 | print (a, n) 35 | 36 | #asdf = hola 37 | if __name__=="__main__": 38 | main(j=1) 39 | 40 | # dafasdfs 41 | -------------------------------------------------------------------------------- /ide2py.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python -*- 2 | 3 | a = Analysis([os.path.join(HOMEPATH,'support\\_mountzlib.py'), 4 | os.path.join(HOMEPATH,'support\\useUnicode.py'), 5 | '..\\rad2py\\ide2py\\main.py', 6 | ], 7 | pathex=['C:\\rad2py\\pyinstaller-1.5.1', 8 | 'C:\\rad2py\\dist\\web2py'], 9 | ) 10 | pyz = PYZ(a.pure) 11 | exe = EXE(pyz, 12 | a.scripts, 13 | exclude_binaries=1, 14 | name=os.path.join('build\\pyi.win32\\ide2py', 'ide2py.exe'), 15 | debug=False, 16 | strip=False, 17 | upx=True, 18 | console=True ) 19 | coll = COLLECT( exe, 20 | a.binaries, 21 | a.zipfiles, 22 | a.datas + [('ide2py.ini.dist', '..\\rad2py\\ide2py\\ide2py.ini.dist', 'DATA')], 23 | strip=False, 24 | upx=True, 25 | name=os.path.join('dist', 'ide2py')) 26 | -------------------------------------------------------------------------------- /ide2py/camera.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | "Camera sensor to track user activity automatically using OpenCV face detection" 5 | 6 | __author__ = "Mariano Reingart (reingart@gmail.com)" 7 | __copyright__ = "Copyright (C) 2014 Mariano Reingart" 8 | __license__ = "GPL 3.0" 9 | 10 | 11 | import os 12 | import wx 13 | import cv2 14 | import time 15 | import warnings 16 | 17 | 18 | class Camera(wx.Panel): 19 | "Widget to capture and display images (with faces detection)" 20 | 21 | def __init__(self, parent, rate=5, width=320, height=240, 22 | classpath="haarcascade_frontalface_default.xml"): 23 | wx.Panel.__init__(self, parent, -1, wx.DefaultPosition, 24 | wx.Size(width, height)) 25 | self.parent = parent 26 | 27 | if not os.path.exists(classpath): 28 | warnings.warn("OpenCV classifier not found: %s" % classpath) 29 | else: 30 | # set up OpenCV features: 31 | self.capture = cv2.VideoCapture(0) 32 | self.capture.set(cv2.cv.CV_CAP_PROP_FRAME_WIDTH, width) 33 | self.capture.set(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT, height) 34 | self.classifier = cv2.CascadeClassifier(classpath) 35 | 36 | # create an initial bitmap with a compatible OpenCV image bit depth 37 | self.bmp = wx.EmptyBitmap(width, height, 24) 38 | 39 | # Initialize sampling capture rate (default: one shot per second) 40 | self.timer = wx.Timer(self) 41 | self.timer.Start(500.) 42 | self.count = 0 43 | self.rate = rate 44 | self.Bind(wx.EVT_PAINT, self.OnPaint) 45 | self.Bind(wx.EVT_TIMER, self.OnTimer) 46 | 47 | def OnTimer(self, evt): 48 | "Capture a single frame image and detect faces on it" 49 | t0 = time.time() 50 | # Capture a image frame twice a second to flush buffers: 51 | self.capture.grab() 52 | ##print "grabbing", self.count 53 | self.count += 1 54 | if self.count % (self.rate * 2): 55 | return 56 | # Process the image frame (default sampling rate is 5 seconds) 57 | t1 = time.time() 58 | ret, img = self.capture.retrieve() 59 | if ret: 60 | # Detect faces using OpenCV (it needs the gray scale conversion): 61 | img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 62 | gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 63 | faces = self.classifier.detectMultiScale( 64 | gray, scaleFactor=1.1, minNeighbors=5, minSize=(85, 85), 65 | flags=cv2.cv.CV_HAAR_SCALE_IMAGE 66 | ) 67 | t2 = time.time() 68 | if False: print "faces", faces, t1-t0, t2-t1 69 | # Draw detected faces over the color original image: 70 | for (x, y, w, h) in faces: 71 | cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 4) 72 | self.bmp.CopyFromBuffer(img) 73 | self.Refresh() 74 | # Notify PSP parent component about the detected change (if any): 75 | if not len(faces): 76 | self.parent.PSPInterrupt("cam") 77 | else: 78 | self.parent.PSPResume("cam") 79 | 80 | def OnPaint(self, evt): 81 | "Draw the captured image to the screen" 82 | dc = wx.BufferedPaintDC(self) 83 | width, height = self.GetSize() 84 | # resize the image up to the panel dimensions, and draw it: 85 | image = wx.ImageFromBitmap(self.bmp) 86 | image = image.Scale(width, height, wx.IMAGE_QUALITY_NORMAL) 87 | dc.DrawBitmap(wx.BitmapFromImage(image), 0, 0) 88 | 89 | 90 | if __name__ == "__main__": 91 | # Test application: 92 | app = wx.App() 93 | frame = wx.Frame(None) 94 | cam = Camera(frame) 95 | frame.SetSize(cam.GetSize()) 96 | frame.Show() 97 | app.MainLoop() 98 | 99 | -------------------------------------------------------------------------------- /ide2py/checker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | "Integration of several python code checkers" 5 | 6 | __author__ = "Mariano Reingart (reingart@gmail.com)" 7 | __copyright__ = "Copyright (C) 2011 Mariano Reingart" 8 | __license__ = "GPL 3.0" 9 | 10 | import os 11 | import compiler 12 | import _ast 13 | 14 | import pep8 15 | import pyflakes.checker 16 | 17 | 18 | # PEP8 Coding Standar 19 | 20 | class PEP8(pep8.Checker): 21 | def __init__(self, filename, lines=None): 22 | self.errors = [] 23 | pep8style = pep8.StyleGuide(parse_argv=False, config_file=False) 24 | options = pep8style.options 25 | options.prog = os.path.basename(filename) 26 | options.exclude = [] 27 | options.filename = filename 28 | options.select = [] 29 | options.ignore = [] 30 | options.verbose = 0 31 | #options.ignore = pep8.DEFAULT_IGNORE.split(',') 32 | options.counters = {'physical lines': 0, 'logical lines': 0,} 33 | options.messages = {} 34 | pep8.Checker.__init__(self, filename) 35 | self.check_all() 36 | 37 | def report_error(self, line_number, offset, text, check): 38 | filename = self.filename 39 | error = dict(summary=text, type=30, 40 | filename=filename, lineno=line_number, offset=offset+1) 41 | self.errors.append(error) 42 | 43 | def __iter__(self): 44 | for error in self.errors: 45 | yield error 46 | 47 | 48 | # PyFlakes Sanity Checks 49 | 50 | class PyFlakes(object): 51 | 52 | def __init__(self, filename): 53 | code_string = open(filename).read() 54 | tree = compile(code_string, filename, "exec", _ast.PyCF_ONLY_AST) 55 | self.checker = pyflakes.checker.Checker(tree, filename) 56 | self.checker.messages.sort(lambda a, b: cmp(a.lineno, b.lineno)) 57 | 58 | def __iter__(self): 59 | for msg in self.checker.messages: 60 | filename = msg.filename 61 | text = msg.message % msg.message_args 62 | lineno = msg.lineno 63 | error = dict(summary=text, type=40, 64 | filename=filename, lineno=lineno, offset=1) 65 | yield error 66 | 67 | 68 | 69 | def check(filename): 70 | "Try all available checkers and return all defects founds" 71 | for defect in PEP8(filename): 72 | yield defect 73 | for defect in PyFlakes(filename): 74 | yield defect 75 | 76 | 77 | if __name__ == '__main__': 78 | for e in check("hola.py"): 79 | print e 80 | 81 | 82 | -------------------------------------------------------------------------------- /ide2py/connector.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | "Connectors for ALM (Application Lifecycle Management)" 5 | 6 | __author__ = "Mariano Reingart (reingart@gmail.com)" 7 | __copyright__ = "Copyright (C) 2014 Mariano Reingart" 8 | __license__ = "GPL 3.0" 9 | 10 | # import SDKs 11 | 12 | try: 13 | # using https://github.com/michaelliao/githubpy/blob/master/github.py 14 | import github 15 | except ImportError: 16 | github = None 17 | 18 | 19 | TAG_MAP = { 20 | 'type': ("bug", "enhancement", "question", "other"), 21 | 'resulution': ("fixed", "duplicate", "wontfix", "help wanted", "invalid"), 22 | } 23 | 24 | 25 | class GitHub(object): 26 | "ide2py extension for integrated GitHub support" 27 | 28 | def __init__(self, username="", password="", organization="", project=""): 29 | 30 | self.gh = github.GitHub(username=username, password=password) 31 | self.repo = self.gh.repos(organization)(project) 32 | self.organization = organization 33 | self.project = project 34 | 35 | def list_tasks(self, status="open"): 36 | "Get a list of tasks availables in the project" 37 | # query github for a list of open tasks 38 | for issue in self.repo.issues.get(state=status, sort='created'): 39 | yield self.build_task(issue) 40 | 41 | def build_task(self, issue): 42 | "Convert a GitHub issue to a Task" 43 | # classify labels (ticket type and resolution) 44 | tags = dict([(key, label['name']) for label in issue['labels'] 45 | for key, values in TAG_MAP.items() 46 | if label['name'] in values]) 47 | # normalize the ticket data 48 | data = { 49 | 'id': issue['id'], 50 | 'title': issue['title'], 51 | 'name': "Issue #%s %s/%s" % (issue['number'], 52 | self.organization, self.project), 53 | 'description': issue['body'], 54 | 'status': issue['state'], 55 | 'type': tags.get('type'), 56 | 'resolution': tags.get('resolution'), 57 | 'owner': issue['user']['login'], 58 | 'started': issue['created_at'], 59 | 'completed': issue['closed_at'], 60 | 'milestone': issue['milestone']['title'] 61 | if issue['milestone'] else '', 62 | } 63 | return data 64 | 65 | def build_issue(self, data): 66 | "Convert the task data to a GitHub issue" 67 | issue = { 68 | 'title': data.get('title'), 69 | 'body': data.get('description'), 70 | } 71 | if 'name' in data: 72 | s = data['name'].split(" ")[1] 73 | issue['number'] = ''.join([s for s in s if s.isdigit()]) 74 | return issue 75 | 76 | def create_task(self, data): 77 | "Create a new a task (GitHub issue)" 78 | issue = self.repo.issues.post(**self.build_issue(data)) 79 | return self.build_task(issue) 80 | 81 | def get_task(self, data): 82 | "Retrieve a specific task (GitHub issue)" 83 | issue = self.build_issue(data) 84 | issue = self.repo.issues(issue['number']).get(**issue) 85 | return self.build_task(issue) 86 | 87 | def update_task(self, data): 88 | "Edit task data (GitHub issue)" 89 | issue = self.build_issue(data) 90 | issue = self.repo.issues(issue['number']).patch(**issue) 91 | return self.build_task(issue) 92 | 93 | 94 | if __name__ == "__main__": 95 | import ConfigParser 96 | import pprint 97 | 98 | config = ConfigParser.ConfigParser() 99 | config.read('ide2py.ini') 100 | 101 | kwargs = dict(config.items("GITHUB")) 102 | kwargs['organization'] = 'reingart' 103 | kwargs['project'] = 'prueba' 104 | print kwargs 105 | gh = GitHub(**kwargs) 106 | task = gh.create_task({'title': 'prueba', 'description': '1234...'}) 107 | pprint.pprint(task) 108 | task['description'] = "56789..." 109 | gh.update_task(task) 110 | pprint.pprint(list(gh.list_tasks(status="open"))) 111 | 112 | -------------------------------------------------------------------------------- /ide2py/diff.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | "Integration of diff tool" 5 | 6 | __author__ = "Mariano Reingart (reingart@gmail.com)" 7 | __copyright__ = "Copyright (C) 2011 Mariano Reingart" 8 | __license__ = "GPL 3.0" 9 | 10 | import os 11 | import difflib 12 | import wx 13 | import wx.html 14 | 15 | 16 | def diff(from_filename, to_filename, context=True, numlines=2): 17 | fromdesc = os.path.basename(from_filename) 18 | todesc= os.path.basename(to_filename) 19 | fromlines = open(from_filename, 'U').readlines() 20 | tolines = open(to_filename, 'U').readlines() 21 | 22 | htmldiff = difflib.HtmlDiff(tabsize=4) 23 | html = htmldiff.make_file(fromlines,tolines,fromdesc,todesc) 24 | return html 25 | 26 | 27 | class SimpleDiff(wx.Frame): 28 | 29 | def __init__(self, html): 30 | wx.Frame.__init__(self, None) 31 | self.ctrl = wx.html.HtmlWindow(self, -1, wx.DefaultPosition, wx.Size(400, 300)) 32 | if "gtk2" in wx.PlatformInfo: 33 | self.ctrl.SetStandardFonts() 34 | self.ctrl.SetPage(html) 35 | self.Show() 36 | #return ctrl 37 | self.SendSizeEvent() 38 | 39 | 40 | if __name__ == '__main__': 41 | 42 | 43 | html = diff("hola.py","hola.py.orig") 44 | print html 45 | open("diff.html","w").write(html) 46 | app = wx.App(redirect=False) 47 | browser = SimpleDiff(html) 48 | #browser.panel.Open(url) 49 | browser.Show() 50 | app.MainLoop() 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /ide2py/fileutil.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | "File facilities enhancing python stdlib or builtins (unicode, line-endings)" 5 | 6 | # Byte-Order-Mark detection inspired by DrPython 7 | 8 | __author__ = "Mariano Reingart (reingart@gmail.com)" 9 | __copyright__ = "Copyright (C) 2011 Mariano Reingart" 10 | __license__ = "GPL 3.0" 11 | 12 | 13 | import codecs 14 | import locale 15 | import re 16 | import wx.stc 17 | 18 | PY_CODING_RE = re.compile(r'coding[:=]\s*([-\w.]+)') 19 | 20 | 21 | def unicode_file_read(f, encoding): 22 | "Detect python encoding or BOM, returns unicode text, encoding, bom, eol" 23 | bom = None 24 | start = 0 25 | # detect encoding 26 | sniff = f.read(240) 27 | match = PY_CODING_RE.search(sniff) 28 | if match: 29 | encoding = match.group(1) 30 | # First 2 to 4 bytes are BOM? 31 | boms = (codecs.BOM, codecs.BOM_BE, codecs.BOM_LE, codecs.BOM_UTF8, 32 | codecs.BOM_UTF16, codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE, 33 | codecs.BOM_UTF32, codecs.BOM_UTF32_BE, codecs.BOM_UTF32_LE) 34 | encodings = ("utf_16", "utf_16_be", "utf_16_le", "utf_8", 35 | "utf_16", "utf_16_be", "utf_16_le", None, None, None) 36 | for i, bom in enumerate(boms): 37 | if sniff[:len(bom)] == bom: 38 | if encoding is None: 39 | encoding = encodings[i] 40 | start = len(bom) 41 | bom = bom 42 | break 43 | else: 44 | # no BOM found, use to platform default if no encoding specified 45 | if not encoding: 46 | encoding = locale.getpreferredencoding() 47 | bom = None 48 | 49 | if not encoding: 50 | raise RuntimeError("Unsupported encoding!") 51 | 52 | # detect line endings ['CRLF', 'CR', 'LF'][self.eol] 53 | # (not always there is a file.newlines -using universal nl support-) 54 | f.seek(start) 55 | line = f.readline() 56 | if line[-2:-1] in ['\n', '\r']: 57 | newlines = line[-2:] 58 | else: 59 | newlines = line[-1:] 60 | if newlines: 61 | eol = {'\r\n': wx.stc.STC_EOL_CRLF, '\n\r': wx.stc.STC_EOL_CRLF, 62 | '\r': wx.stc.STC_EOL_CR, 63 | '\n': wx.stc.STC_EOL_LF}[newlines] 64 | else: 65 | eol = wx.stc.STC_EOL_CRLF 66 | newlines = '\r\n' 67 | # rewind and read text (in the proper encoding) 68 | f.seek(start) 69 | return f.read().decode(encoding), encoding, bom, eol, newlines 70 | 71 | -------------------------------------------------------------------------------- /ide2py/gui2py.py: -------------------------------------------------------------------------------- 1 | "Basic Proof-of-Concept for GUI design integration" 2 | 3 | import sys,os 4 | import wx 5 | import wx.lib.agw.aui as aui 6 | 7 | GUI2PY_PATH = "../../gui2py" 8 | 9 | class Gui2pyMixin: 10 | 11 | def __init__(self, ): 12 | 13 | try: 14 | try: 15 | import gui 16 | except ImportError: 17 | sys.path.append(GUI2PY_PATH) 18 | 19 | # import controls (fill the registry!) 20 | from gui import Window 21 | 22 | # import tools used by the designer 23 | from gui.tools.inspector import InspectorPanel 24 | from gui.tools.propeditor import PropertyEditorPanel 25 | from gui.tools.designer import BasicDesigner 26 | from gui.tools.toolbox import ToolBox 27 | 28 | # create the windows and the property editor / inspector 29 | log = sys.stdout 30 | 31 | self.propeditor = PropertyEditorPanel(self, log) 32 | self.inspector = InspectorPanel(self, self.propeditor, log) 33 | 34 | self._mgr.AddPane(self.propeditor, aui.AuiPaneInfo().Name("propeditor"). 35 | Caption("Property Editor").Float().FloatingSize(wx.Size(400, 100)). 36 | FloatingPosition(self.GetStartPosition()).DestroyOnClose(False).PinButton(True). 37 | MinSize((100, 100)).Right().Bottom().MinimizeButton(True)) 38 | 39 | self._mgr.AddPane(self.inspector, aui.AuiPaneInfo().Name("inspector"). 40 | Caption("inspector").Float().FloatingSize(wx.Size(400, 100)). 41 | FloatingPosition(self.GetStartPosition()).DestroyOnClose(False).PinButton(True). 42 | MinSize((100, 100)).Right().Bottom().MinimizeButton(True)) 43 | 44 | self.toolbox = ToolBox(self) 45 | 46 | self._mgr.AddPane(self.toolbox, aui.AuiPaneInfo().Name("toolbox"). 47 | ToolbarPane().Left().Position(2)) 48 | 49 | except ImportError, e: 50 | self.ShowInfoBar(u"cannot import gui2py!: %s" % unicode(e), 51 | flags=wx.ICON_ERROR, key="gui2py") 52 | 53 | def OnDesigner(self, evt): 54 | try: 55 | import gui 56 | from gui.tools.designer import BasicDesigner 57 | from gui.tools.toolbox import ToolBox, set_drop_target 58 | 59 | filename = os.path.join(GUI2PY_PATH, "sample.pyw") 60 | vars = {} 61 | execfile(filename, vars) 62 | w = None 63 | for name, value in vars.items(): 64 | if not isinstance(value, gui.Window): 65 | continue 66 | w = value # TODO: support many windows 67 | # load the window in the widget inspector 68 | self.inspector.load_object(w) 69 | # associate the window with the designer: 70 | # (override mouse events to allow moving and resizing) 71 | self.designer = BasicDesigner(w, self.inspector) 72 | # associate the window with the toolbox: 73 | # (this will allow to drop new controls on the window) 74 | set_drop_target(w, w, self.designer, self.inspector) 75 | # link the designer (context menu) 76 | self.inspector.set_designer(self.designer) 77 | w.show() 78 | 79 | ##w.onunload = save 80 | 81 | # this does not work: 82 | #w.wx_obj.Reparent(self) 83 | ##self._mgr.AddPane(w.wx_obj, aui.AuiPaneInfo().Name("UserWindow"). 84 | ## Float()) 85 | 86 | w.stay_on_top = True # assure the windows stay on top of AUI 87 | w.show() 88 | 89 | except ImportError, e: 90 | self.ShowInfoBar(u"cannot import gui2py!: %s" % unicode(e), 91 | flags=wx.ICON_ERROR, key="gui2py") 92 | 93 | -------------------------------------------------------------------------------- /ide2py/gui_designer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | "Integrated Simple GUI Designer (using gui2py -wx.HTML-)" 5 | 6 | __author__ = "Mariano Reingart (reingart@gmail.com)" 7 | __copyright__ = "Copyright (C) 2011 Mariano Reingart" 8 | __license__ = "GPL 3.0" 9 | 10 | import os 11 | import sys 12 | import wx 13 | import wx.lib.agw.flatnotebook as fnb 14 | import wx.html 15 | import wx.lib.wxpTag 16 | 17 | 18 | SAMPLE_GUI_WXHTML = """ 19 | 20 | 21 | 22 | 23 | 24 | 25 | """ 26 | 27 | class GUIPanel(wx.Panel): 28 | 29 | def __init__(self, parent): 30 | wx.Panel.__init__(self, parent) 31 | 32 | sizer = wx.BoxSizer(wx.VERTICAL) 33 | self.SetSizer(sizer) 34 | 35 | bookstyle = fnb.FNB_NODRAG | fnb.FNB_SMART_TABS 36 | self.book = fnb.FlatNotebook(self, wx.ID_ANY, agwStyle=bookstyle) 37 | 38 | sizer.Add(self.book,1, wx.ALL | wx.EXPAND) 39 | 40 | # Add some pages to the second notebook 41 | 42 | self.text = wx.TextCtrl(self.book, -1, SAMPLE_GUI_WXHTML, style=wx.TE_MULTILINE) 43 | self.book.AddPage(self.text, "Edition") 44 | 45 | self.html = wx.html.HtmlWindow(self, -1, wx.DefaultPosition, wx.Size(400, 300)) 46 | if "gtk2" in wx.PlatformInfo: 47 | self.html.SetStandardFonts() 48 | self.book.AddPage(self.html, "Preview") 49 | 50 | sizer.Layout() 51 | self.SendSizeEvent() 52 | 53 | self.Bind(fnb.EVT_FLATNOTEBOOK_PAGE_CHANGING, self.OnPageChanging) 54 | self.Bind(fnb.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged) 55 | 56 | #self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) 57 | 58 | def OnPageChanging(self, event): 59 | page = event.GetOldSelection() 60 | if page==0: 61 | # save current cursor position 62 | self.sel = self.text.GetSelection() 63 | if page==1: 64 | # restore previous selection (really needed?) 65 | self.text.SetSelection(*self.sel) 66 | event.Skip() 67 | 68 | def OnPageChanged(self, event): 69 | page = event.GetSelection() 70 | if page==0: 71 | wx.CallAfter(self.text.SetFocus) 72 | if page==1: 73 | self.html.SetPage(self.text.GetValue()) 74 | event.Skip() 75 | 76 | 77 | class SimpleGUI(wx.Frame): 78 | 79 | def __init__(self): 80 | wx.Frame.__init__(self, None) 81 | 82 | self.panel = GUIPanel(self) 83 | self.Show() 84 | 85 | if __name__ == '__main__': 86 | app = wx.App(redirect=False) 87 | browser = SimpleGUI() 88 | #browser.panel.Open(url) 89 | app.MainLoop() 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /ide2py/ide2py.ini.dist: -------------------------------------------------------------------------------- 1 | [REPOSITORY] 2 | username = Your Name 3 | type = hg 4 | 5 | [WEB2PY] 6 | # full (or relative) path to web2py installation folder 7 | path = ../web2py 8 | # admin password: 9 | password = a 10 | # port (default 8006): 11 | port = 8006 12 | 13 | [AUI] 14 | perspective = 15 | maximize = True 16 | 17 | [CHECKER] 18 | pep8 = True 19 | doctest = True 20 | pyflakes = True 21 | 22 | [EDITOR] 23 | face_helv = DejaVu Serif 24 | view_eol = False 25 | face_other = DejaVu 26 | calltips = True 27 | encoding = utf_8 28 | face_size = 10 29 | view_white_space = False 30 | edge_column = 79 31 | face_times = DejaVu Sans 32 | autocomplete = True 33 | eol_mode = 0 34 | face_mono = DejaVu Sans Mono 35 | set_identation_guides = True 36 | use_tabs = False 37 | face_size2 = 8 38 | tab_width = 4 39 | 40 | [PSP] 41 | phases = Planning, Design, Code, Compile, Test, Review, Postmortem 42 | data = psp-data.dat 43 | metadata = metadata 44 | checklist = psp-checklist.dat 45 | server_url = http://localhost:8000/psp2py/services/call/jsonrpc 46 | wiki_url = http://localhost:8000/psp2py/wiki/load/index 47 | psp2py_url = http://localhost:8000/psp2py 48 | 49 | [MAIN] 50 | settings = settings.pkl 51 | 52 | [STC.PY] 53 | stc_p_triple = face:%(mono)s,fore:#7F0000,size:%(size)d 54 | stc_p_tripledouble = face:%(mono)s,fore:#7F0000,size:%(size)d 55 | stc_p_number = face:%(mono)s,fore:#007F7F,size:%(size)d 56 | stc_p_word = face:%(mono)s,fore:#00007F,bold,size:%(size)d 57 | stc_p_stringeol = face:%(mono)s,fore:#000000,face:%(mono)s,back:#E0C0E0,eol,size:%(size)d 58 | stc_style_default = face:%(mono)s,size:%(size)d 59 | stc_style_linenumber = back:#C0C0C0,face:%(mono)s,size:%(size2)d 60 | stc_style_bracebad = fore:#000000,back:#FF0000,bold 61 | stc_style_bracelight = fore:#FFFFFF,back:#0000FF,bold 62 | stc_p_commentline = face:%(mono)s,fore:#007F00,back:#E8FFE8,italic,size:%(size)d 63 | stc_style_controlchar = face:%(mono)s 64 | stc_p_identifier = face:%(mono)s,size:%(size)d 65 | stc_p_string = face:%(mono)s,fore:#7F007F,size:%(size)d 66 | stc_p_character = face:%(mono)s,fore:#7F007F,size:%(size)d 67 | stc_p_default = face:%(mono)s,size:%(size)d 68 | stc_p_commentblock = face:%(mono)s,fore:#990000,back:#C0C0C0,italic,size:%(size)d 69 | stc_p_classname = face:%(mono)s,fore:#0000FF,bold,underline,size:%(size)d 70 | stc_p_operator = face:%(mono)s,bold,size:%(size)d 71 | stc_p_defname = face:%(mono)s,fore:#007F7F,bold,size:%(size)d 72 | stc_p_decorator = face:%(mono)s,fore:#007F7F,bold,size:%(size)d 73 | 74 | [HISTORY] 75 | 76 | [DATABASE] 77 | PATH = local.db 78 | 79 | [GITHUB] 80 | username = 81 | password = 82 | 83 | -------------------------------------------------------------------------------- /ide2py/rad2py.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/ide2py/rad2py.ico -------------------------------------------------------------------------------- /ide2py/repo_hg.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | "Integration of mercurial hg version control repository" 5 | 6 | __author__ = "Mariano Reingart (reingart@gmail.com)" 7 | __copyright__ = "Copyright (C) 2011 Mariano Reingart" 8 | __license__ = "GPL 3.0" 9 | 10 | import os 11 | 12 | from mercurial import ui, hg, cmdutil 13 | 14 | try: 15 | from mercurial.cmdutil import revpair, revsingle, match 16 | except ImportError: 17 | # mercurial version 1.9 API changes 18 | from mercurial.scmutil import revpair, revsingle, match as hg_match 19 | match = lambda repo, paths: hg_match(repo[None], paths) 20 | 21 | # based on web2py mercurial support 22 | 23 | _hgignore_content = """\ 24 | syntax: glob 25 | *~ 26 | *.pyc 27 | *.pyo 28 | *.bak 29 | cache/* 30 | databases/* 31 | sessions/* 32 | errors/* 33 | """ 34 | 35 | 36 | class MercurialRepo(object): 37 | 38 | def __init__(self, path, username): 39 | uio = ui.ui() 40 | uio.quiet = True 41 | if not os.environ.get('HGUSER') and not uio.config("ui", "username"): 42 | os.environ['HGUSER'] = username 43 | try: 44 | repo = hg.repository(ui=uio, path=path) 45 | except: 46 | repo = hg.repository(ui=uio, path=path, create=True) 47 | hgignore = os.path.join(path, '.hgignore') 48 | if not os.path.exists(hgignore): 49 | open(hgignore, 'w').write(_hgignore_content) 50 | self.repo = repo 51 | self.decode = None 52 | 53 | def add(self, filepaths, dry_run=False, subrepo=None): 54 | "add the specified files on the next commit" 55 | m = match(self.repo, filepaths) 56 | prefix = "" 57 | uio = self.repo.ui 58 | rejected = cmdutil.add(uio, self.repo, m, dry_run, subrepo, prefix) 59 | return rejected 60 | 61 | def commit(self, filepaths, message): 62 | "commit the specified files or all outstanding changes" 63 | oldid = self.repo[self.repo.lookup('.')] 64 | user = date = None 65 | m = match(self.repo, filepaths) 66 | node = self.repo.commit(message, user, date, m) 67 | if self.repo[self.repo.lookup('.')] == oldid: 68 | return None # no changes 69 | return True # sucess 70 | 71 | def cat(self, file1, revision=None): 72 | "return the current or given revision of a file" 73 | ctx = revsingle(self.repo, revision) 74 | m = match(self.repo, (file1,)) 75 | for abs in ctx.walk(m): 76 | data = ctx[abs].data() 77 | if self.decode: 78 | data = self.repo.wwritedata(abs, data) 79 | return data 80 | 81 | def history(self): 82 | for file in self.repo[self.repo.lookup('.')].files(): 83 | print file 84 | 85 | for change in self.repo.changelog: 86 | ctx=self.repo.changectx(change) 87 | revision, description = ctx.rev(), ctx.description() 88 | print revision, description 89 | 90 | def remove(self, filepaths): 91 | "remove the specified files on the next commit" 92 | raise NotImplementedError("HG remove is not implemented!") 93 | 94 | def revert(self, revision=None): 95 | ctx = revsingle(self.repo, revision) 96 | hg.update(self.repo, revision) 97 | print "reverted to revision %s" % ctx.rev() 98 | 99 | def status(self, path=None): 100 | "show changed files in the working directory" 101 | 102 | revs = None 103 | node1, node2 = revpair(self.repo, revs) 104 | 105 | cwd = (path and self.repo.getcwd()) or '' 106 | copy = {} 107 | states = 'modified added removed deleted unknown ignored clean'.split() 108 | show = states 109 | 110 | stat = self.repo.status(node1, node2, match(self.repo, path), 111 | 'ignored' in show, 'clean' in show, 'unknown' in show, 112 | ) 113 | changestates = zip(states, 'MAR!?IC', stat) 114 | 115 | for state, char, files in changestates: 116 | for f in files: 117 | yield f, state 118 | #repo.wjoin(abs), self.repo.pathto(f, cwd) 119 | 120 | def rollback(self, dry_run=None): 121 | "Undo the last transaction (dangerous): commit, import, pull, push, ..." 122 | return self.repo.rollback(dry_run) 123 | 124 | 125 | if __name__ == '__main__': 126 | import sys 127 | 128 | r = MercurialRepo("..") 129 | if '--status' in sys.argv: 130 | for st, fn in r.status(): 131 | print st, fn 132 | if '--cat' in sys.argv: 133 | print r.cat("hola.py", rev=False) 134 | if '--commit' in sys.argv: 135 | ret = r.commit(["hola.py"], "test commit!") 136 | print "result", ret 137 | if '--add' in sys.argv: 138 | print r.add(["pyi25.py"]) 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /ide2py/repo_w2p.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | "Integration of web2py remote admin" 5 | 6 | __author__ = "Mariano Reingart (reingart@gmail.com)" 7 | __copyright__ = "Copyright (C) 2011 Mariano Reingart" 8 | __license__ = "GPL 3.0" 9 | 10 | import os 11 | 12 | import simplejsonrpc 13 | 14 | 15 | class Web2pyRepo(object): 16 | 17 | def __init__(self, server_url, username=""): 18 | self.server_url = server_url 19 | # connect to web2py server: 20 | self.w2p_rpc_client = simplejsonrpc.ServiceProxy(server_url, 21 | verbose=True) 22 | 23 | def add(self, filepaths, dry_run=False, subrepo=None): 24 | "add the specified files on the next commit" 25 | return 26 | 27 | def commit(self, filepaths, message): 28 | "commit the specified files or all outstanding changes" 29 | return False # fail 30 | 31 | def cat(self, file1, revision=None): 32 | "return the current or given revision of a file" 33 | if file1.startswith(self.server_url): 34 | file1 = file1[len(self.server_url)+1:] 35 | if file1.startswith("/"): 36 | file1 = file1[1:] 37 | return self.w2p_rpc_client.read_file(file1) 38 | 39 | def put(self, file1, data): 40 | "write the file contents" 41 | if file1.startswith(self.server_url): 42 | file1 = file1[len(self.server_url)+1:] 43 | if file1.startswith("/"): 44 | file1 = file1[1:] 45 | return self.w2p_rpc_client.write_file(file1, data) 46 | 47 | def history(self): 48 | raise NotImplementedError("web2py repo history is not implemented!") 49 | 50 | def remove(self, filepaths): 51 | "remove the specified files on the next commit" 52 | raise NotImplementedError("web2py repo remove is not implemented!") 53 | 54 | def revert(self, revision=None): 55 | raise NotImplementedError("web2py repo revert is not implemented!") 56 | 57 | def status(self, path=None): 58 | "show changed files in the working directory" 59 | 60 | for app in self.w2p_rpc_client.list_apps(): 61 | #yield app, 'clean' 62 | for filename in self.w2p_rpc_client.list_files(app): 63 | yield "%s/%s" % (app, filename), 'clean' 64 | 65 | def rollback(self, dry_run=None): 66 | "Undo the last transaction (dangerous): commit, import, pull, push, ..." 67 | raise NotImplementedError("web2py repo rollback is not implemented!") 68 | 69 | 70 | if __name__ == '__main__': 71 | import sys 72 | 73 | url = "http://admin:a@localhost:8000/admin/webservices/call/jsonrpc" 74 | try: 75 | r = Web2pyRepo(url) 76 | if '--status' in sys.argv: 77 | for st, fn in r.status(): 78 | print st, fn 79 | if '--cat' in sys.argv: 80 | print r.cat(url+"/welcome/controllers/default.py") 81 | if '--commit' in sys.argv: 82 | ret = r.commit(["hola.py"], "test commit!") 83 | print "result", ret 84 | if '--add' in sys.argv: 85 | print r.add(["pyi25.py"]) 86 | except simplejsonrpc.JSONRPCError, e: 87 | print "=" * 80 88 | print str(e) 89 | print "-" * 80 90 | print e.data 91 | print "-" * 80 92 | 93 | 94 | -------------------------------------------------------------------------------- /ide2py/shell.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | "Integrated WxPython Shell" 5 | 6 | __author__ = "Mariano Reingart (reingart@gmail.com)" 7 | __copyright__ = "Copyright (C) 2011 Mariano Reingart" 8 | __license__ = "GPL 3.0" 9 | 10 | # based on picalo implementation 11 | # new __main__ based on web2py.gluon.contrib.shell (google GAE) 12 | 13 | 14 | import new 15 | import time 16 | import os 17 | import traceback 18 | import sys 19 | 20 | import StringIO 21 | import wx.py 22 | 23 | 24 | class Interpreter(wx.py.interpreter.Interpreter): 25 | "Customized interpreter for local execution (handling locals and globals)" 26 | 27 | def __init__(self, locals, rawin, stdin, stdout, stderr, 28 | ps1='>>> ', ps2='... ', 29 | globals=None, 30 | debugger=None, 31 | ): 32 | """Create an interactive interpreter object.""" 33 | wx.py.interpreter.Interpreter.__init__(self, locals=locals, rawin=rawin, 34 | stdin=stdin, stdout=stdout, stderr=stderr) 35 | sys.ps1 = ps1 36 | sys.ps2 = ps2 37 | self.globals = globals or {} 38 | self.debugger = debugger 39 | 40 | def runsource(self, source): 41 | """Compile and run source code in the interpreter.""" 42 | if self.debugger: 43 | text = self.debugger.current.Exec(source, 44 | write=self.stdout.write, 45 | readline=self.stdin.readline) 46 | self.stdout.write(text) 47 | self.stdout.write(os.linesep) 48 | return False # no line continuation in debug mode by now 49 | else: 50 | return wx.py.interpreter.Interpreter.runsource(self, 51 | source) 52 | 53 | 54 | def getAutoCompleteList(self, command='', *args, **kwds): 55 | root = wx.py.introspect.getRoot(command, terminator=".") 56 | if self.debugger: 57 | l = self.debugger.current.GetAutoCompleteList(root) 58 | else: 59 | l = wx.py.interpreter.Interpreter.getAutoCompleteList(self, 60 | command, *args, **kwds) 61 | return l 62 | 63 | def getCallTip(self, command='', *args, **kwds): 64 | root = wx.py.introspect.getRoot(command, terminator="(") 65 | if self.debugger: 66 | calltip = self.debugger.current.GetCallTip(root) 67 | else: 68 | calltip = wx.py.interpreter.Interpreter.getCallTip(self, 69 | command, *args, **kwds) 70 | return calltip 71 | 72 | 73 | class Shell(wx.py.shell.Shell): 74 | "Customized version of PyShell" 75 | def __init__(self, parent, debugger): 76 | wx.py.shell.Shell.__init__(self, parent, InterpClass=Interpreter, 77 | debugger=debugger) 78 | self.console = None 79 | self.has_focus = False 80 | self.Bind(wx.EVT_SET_FOCUS, self.OnFocus) 81 | self.Bind(wx.EVT_KILL_FOCUS, self.OnFocus) 82 | 83 | def onCut(self, event=None): 84 | self.Cut() 85 | def onCopy(self, event=None): 86 | self.Copy() 87 | def onPaste(self, event=None): 88 | self.Paste() 89 | def onSelectAll(self, event=None): 90 | self.SelectAll() 91 | 92 | def raw_input(self, prompt=""): 93 | "Return string based on user input (in a separate console if available)" 94 | if self.console: 95 | if prompt: 96 | self.console.write(prompt) 97 | return self.console.readline() 98 | else: 99 | return wx.py.shell.Shell.raw_input(self, prompt) 100 | 101 | def OnFocus(self, evt): 102 | self.has_focus = evt.GetId() == wx.EVT_SET_FOCUS 103 | evt.Skip() 104 | 105 | def HasFocus(self): 106 | "emulate HasFocus for older wxpython versions" 107 | return self.has_focus 108 | 109 | def OnKeyDown(self, event): 110 | "Handle key presses (overrides Shell default method)" 111 | key = event.GetKeyCode() 112 | # use arrow up/down for history and tab for autocompletion 113 | # if autocomplete/calltip is active, proced with normal behavior 114 | if self.AutoCompActive() and not self.CallTipActive(): 115 | super(Shell, self).OnKeyDown(event) 116 | elif key == wx.WXK_UP and self.CanEdit(): 117 | self.OnHistoryReplace(step=+1) 118 | elif key == wx.WXK_DOWN and self.CanEdit(): 119 | self.OnHistoryReplace(step=-1) 120 | elif key == wx.WXK_TAB and \ 121 | self.GetCurrentPos() != self.promptPosEnd and \ 122 | self.CanEdit(): 123 | # try to autocomplete: 124 | self.OnCallTipAutoCompleteManually(event.ShiftDown()) 125 | if not self.AutoCompActive() and not self.CallTipActive(): 126 | # show autocomplete form history 127 | self.OnShowCompHistory() 128 | else: 129 | super(Shell, self).OnKeyDown(event) 130 | -------------------------------------------------------------------------------- /ide2py/simplejsonrpc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # This program is free software; you can redistribute it and/or modify 4 | # it under the terms of the GNU Lesser General Public License as published by the 5 | # Free Software Foundation; either version 3, or (at your option) any later 6 | # version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but 9 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY 10 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 11 | # for more details. 12 | 13 | "Pythonic simple JSON RPC Client implementation" 14 | 15 | __author__ = "Mariano Reingart (reingart@gmail.com)" 16 | __copyright__ = "Copyright (C) 2011 Mariano Reingart" 17 | __license__ = "LGPL 3.0" 18 | __version__ = "0.05" 19 | 20 | 21 | import urllib 22 | from xmlrpclib import Transport, SafeTransport 23 | from cStringIO import StringIO 24 | import random 25 | import sys 26 | try: 27 | import gluon.contrib.simplejson as json # try web2py json serializer 28 | except ImportError: 29 | try: 30 | import json # try stdlib (py2.6) 31 | except: 32 | import simplejson as json # try external module 33 | 34 | 35 | class JSONRPCError(RuntimeError): 36 | "Error object for remote procedure call fail" 37 | def __init__(self, code, message, data=None): 38 | value = "%s: %s\n%s" % (code, message, '\n'.join(data)) 39 | RuntimeError.__init__(self, value) 40 | self.code = code 41 | self.message = message 42 | self.data = data 43 | 44 | 45 | class JSONDummyParser: 46 | "json wrapper for xmlrpclib parser interfase" 47 | def __init__(self): 48 | self.buf = StringIO() 49 | def feed(self, data): 50 | self.buf.write(data) 51 | def close(self): 52 | return self.buf.getvalue() 53 | 54 | 55 | class JSONTransportMixin: 56 | "json wrapper for xmlrpclib transport interfase" 57 | 58 | def send_content(self, connection, request_body): 59 | connection.putheader("Content-Type", "application/json") 60 | connection.putheader("Content-Length", str(len(request_body))) 61 | connection.endheaders() 62 | if request_body: 63 | connection.send(request_body) 64 | # todo: add gzip compression 65 | 66 | def getparser(self): 67 | # get parser and unmarshaller 68 | parser = JSONDummyParser() 69 | return parser, parser 70 | 71 | 72 | class JSONTransport(JSONTransportMixin, Transport): 73 | pass 74 | 75 | class JSONSafeTransport(JSONTransportMixin, SafeTransport): 76 | pass 77 | 78 | 79 | class ServerProxy(object): 80 | "JSON RPC Simple Client Service Proxy" 81 | 82 | def __init__(self, uri, transport=None, encoding=None, verbose=0): 83 | self.location = uri # server location (url) 84 | self.trace = verbose # show debug messages 85 | self.exceptions = True # raise errors? (JSONRPCError) 86 | self.timeout = None 87 | self.json_request = self.json_response = '' 88 | 89 | type, uri = urllib.splittype(uri) 90 | if type not in ("http", "https"): 91 | raise IOError, "unsupported JSON-RPC protocol" 92 | self.__host, self.__handler = urllib.splithost(uri) 93 | 94 | if transport is None: 95 | if type == "https": 96 | transport = JSONSafeTransport() 97 | else: 98 | transport = JSONTransport() 99 | self.__transport = transport 100 | self.__encoding = encoding 101 | self.__verbose = verbose 102 | 103 | def __getattr__(self, attr): 104 | "pseudo method that can be called" 105 | return lambda *args: self.call(attr, *args) 106 | 107 | def call(self, method, *args): 108 | "JSON RPC communication (method invocation)" 109 | 110 | # build data sent to the service 111 | request_id = random.randint(0, sys.maxint) 112 | data = {'id': request_id, 'method': method, 'params': args, } 113 | request = json.dumps(data) 114 | 115 | # make HTTP request (retry if connection is lost) 116 | response = self.__transport.request( 117 | self.__host, 118 | self.__handler, 119 | request, 120 | verbose=self.__verbose 121 | ) 122 | 123 | # store plain request and response for further debugging 124 | self.json_request = request 125 | self.json_response = response 126 | 127 | # parse json data coming from service 128 | # {'version': '1.1', 'id': id, 'result': result, 'error': None} 129 | response = json.loads(response) 130 | 131 | if response['id'] != request_id: 132 | raise JSONRPCError(0, "JSON Request ID != Response ID") 133 | 134 | self.error = response.get('error', {}) 135 | if self.error and self.exceptions: 136 | raise JSONRPCError(self.error.get('code', 0), 137 | self.error.get('message', ''), 138 | self.error.get('data', None)) 139 | 140 | return response.get('result') 141 | 142 | 143 | ServiceProxy = ServerProxy 144 | 145 | 146 | if __name__ == "__main__": 147 | # basic tests: 148 | location = "http://www.web2py.com.ar/webservices/sample/call/jsonrpc" 149 | client = ServerProxy(location, verbose='--verbose' in sys.argv,) 150 | print client.add(1, 2) 151 | 152 | -------------------------------------------------------------------------------- /ide2py/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/ide2py/splash.png -------------------------------------------------------------------------------- /ide2py/tester.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | "Integration of several python testers" 5 | 6 | __author__ = "Mariano Reingart (reingart@gmail.com)" 7 | __copyright__ = "Copyright (C) 2011 Mariano Reingart" 8 | __license__ = "GPL 3.0" 9 | 10 | import os 11 | import wx 12 | 13 | from doctest import DocTestParser, DocTestRunner 14 | 15 | 16 | # Python Basic DocTest Suite 17 | 18 | def doctestfile(filename, module_relative=True, name=None, package=None, 19 | globs=None, verbose=None, report=True, optionflags=0, 20 | extraglobs=None, raise_on_error=False, parser=DocTestParser(), 21 | encoding=None, compileflags=None): 22 | 23 | text = open(filename).read() 24 | 25 | # If no name was given, then use the file's name. 26 | if name is None: 27 | name = os.path.basename(filename) 28 | 29 | # Assemble the globals. 30 | if globs is None: 31 | globs = {} 32 | else: 33 | globs = globs.copy() 34 | if extraglobs is not None: 35 | globs.update(extraglobs) 36 | 37 | defects = [] 38 | class CustomDocTestRunner(DocTestRunner): 39 | def report_failure(self, out, test, example, got): 40 | text = "doctest for %s failed, want %s got %s" % ( 41 | example.source.strip(), example.want.split()[0], got) 42 | lineno = example.lineno 43 | error = dict(summary=text, type=60, 44 | filename=filename, lineno=lineno+1, offset=1) 45 | defects.append(error) 46 | 47 | def report_unexpected_exception(self, out, test, example, exc_info): 48 | text = "doctest for %s failed, exception %s" % ( 49 | example.source.strip(), repr(exc_info[1])) 50 | lineno = example.lineno 51 | error = dict(summary=text, type=80, 52 | filename=filename, lineno=lineno+1, offset=1) 53 | defects.append(error) 54 | 55 | runner = CustomDocTestRunner(verbose=verbose, optionflags=optionflags) 56 | 57 | if encoding is not None: 58 | text = text.decode(encoding) 59 | 60 | # compile and execute the file to get the global definitions 61 | exec compile(text, filename, "exec") in globs 62 | 63 | # Read the file, convert it to a test, and run it. 64 | tests = parser.get_doctest(text, globs, name, filename, 0) 65 | 66 | count, fail = runner.run(tests) 67 | 68 | if not count: 69 | wx.MessageBox("No doc tests could be run from: %s module" % filename, "No doc tests found", wx.ICON_ERROR) 70 | 71 | for defect in defects: 72 | yield defect 73 | 74 | def unittestfile(filename): 75 | # unit test using pytong: http://code.google.com/p/pytong 76 | import pytong 77 | pytong.filename = filename 78 | frame = pytong.Frame("PyTong: %s" % filename) 79 | if frame.Load(): 80 | # show and start all tests (get root) 81 | frame.Show() 82 | item = frame.tree.GetRootItem() 83 | frame.RunItem(item) 84 | while frame.running: 85 | wx.YieldIfNeeded() 86 | for result in frame.results: 87 | if not result.wasSuccessful(): 88 | for error in result.errors: 89 | defect = dict(summary="ERROR IN " + error[0].__str__(), 90 | description=error[1], 91 | type=60, filename=filename, lineno=None, offset=1) 92 | yield defect 93 | for error in result.failures: 94 | defect = dict(summary="FAILURE IN " + error[0].__str__(), 95 | description=error[1], 96 | type=60, filename=filename, lineno=None, offset=1) 97 | yield defect 98 | else: 99 | frame.Destroy() 100 | 101 | def test(filename): 102 | "Try all available testers and return all defects founds" 103 | cdir, filen = os.path.split(os.path.abspath(filename)) 104 | print cdir, filen, os.path.abspath(filename) 105 | if not cdir: 106 | cdir = "." 107 | cwd = os.getcwd() 108 | try: 109 | os.chdir(cdir) 110 | 111 | for defect in doctestfile(filename): 112 | yield defect 113 | 114 | for defect in unittestfile(filename): 115 | yield defect 116 | 117 | finally: 118 | os.chdir(cwd) 119 | 120 | 121 | if __name__ == '__main__': 122 | for e in test("hola.py"): 123 | print e 124 | 125 | 126 | -------------------------------------------------------------------------------- /ide2py/wiki.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | "Integrated Simple Wiki Text WYSWYG Edit Control (using web2py markmin)" 5 | 6 | __author__ = "Mariano Reingart (reingart@gmail.com)" 7 | __copyright__ = "Copyright (C) 2011 Mariano Reingart" 8 | __license__ = "GPL 3.0" 9 | 10 | import os 11 | import sys 12 | import wx 13 | import wx.lib.agw.flatnotebook as fnb 14 | import wx.html 15 | 16 | 17 | try: 18 | sys.path.append(os.path.abspath("../web2py")) 19 | from gluon.contrib.markmin.markmin2html import render 20 | except ImportError: 21 | raise 22 | render = lambda x: x 23 | 24 | 25 | SAMPLE_WIKI_TEXT = """ 26 | # Markmin Examples 27 | 28 | ## Bold, italic, code and links 29 | 30 | **bold** ''italic'' ``verbatim`` http://google.com [[click me #myanchor]] 31 | 32 | [[title link]] 33 | 34 | ## Images 35 | 36 | [[some image http://www.web2py.com/examples/static/web2py_logo.png right 200px]] 37 | 38 | ## Unordered Lists 39 | - Dog 40 | - Cat 41 | - Mouse 42 | 43 | ## Ordered Lists 44 | + Dog 45 | + Cat 46 | + Mouse 47 | 48 | ## Tables 49 | 50 | --------- 51 | **A** | **B** | **C** 52 | 0 | 0 | X 53 | 0 | X | 0 54 | X | 0 | 0 55 | -----:abc 56 | 57 | ### Blockquote 58 | ----- 59 | Hello world 60 | ----- 61 | ### Code, ````, escaping and extra stuff 62 | `` 63 | def test(): 64 | return "this is Python code" 65 | ``:python 66 | 67 | """ 68 | 69 | class WikiPanel(wx.Panel): 70 | 71 | def __init__(self, parent): 72 | wx.Panel.__init__(self, parent) 73 | 74 | sizer = wx.BoxSizer(wx.VERTICAL) 75 | self.SetSizer(sizer) 76 | 77 | bookstyle = fnb.FNB_NODRAG | fnb.FNB_SMART_TABS 78 | self.book = fnb.FlatNotebook(self, wx.ID_ANY, agwStyle=bookstyle) 79 | 80 | sizer.Add(self.book,1, wx.ALL | wx.EXPAND) 81 | 82 | # Add some pages to the second notebook 83 | 84 | self.text = wx.TextCtrl(self.book, -1, SAMPLE_WIKI_TEXT, style=wx.TE_MULTILINE) 85 | self.book.AddPage(self.text, "Edition") 86 | 87 | self.html = wx.html.HtmlWindow(self, -1, wx.DefaultPosition, wx.Size(400, 300)) 88 | if "gtk2" in wx.PlatformInfo: 89 | self.html.SetStandardFonts() 90 | self.book.AddPage(self.html, "Preview") 91 | 92 | sizer.Layout() 93 | self.SendSizeEvent() 94 | 95 | self.Bind(fnb.EVT_FLATNOTEBOOK_PAGE_CHANGING, self.OnPageChanging) 96 | self.Bind(fnb.EVT_FLATNOTEBOOK_PAGE_CHANGED, self.OnPageChanged) 97 | 98 | #self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) 99 | 100 | def OnPageChanging(self, event): 101 | page = event.GetOldSelection() 102 | if page==0: 103 | # save current cursor position 104 | self.sel = self.text.GetSelection() 105 | if page==1: 106 | # restore previous selection (really needed?) 107 | self.text.SetSelection(*self.sel) 108 | event.Skip() 109 | 110 | def OnPageChanged(self, event): 111 | page = event.GetSelection() 112 | if page==0: 113 | wx.CallAfter(self.text.SetFocus) 114 | if page==1: 115 | self.html.SetPage(render(self.text.GetValue())) 116 | event.Skip() 117 | 118 | 119 | class SimpleWiki(wx.Frame): 120 | 121 | def __init__(self): 122 | wx.Frame.__init__(self, None) 123 | 124 | self.panel = WikiPanel(self) 125 | self.Show() 126 | 127 | if __name__ == '__main__': 128 | app = wx.App() 129 | browser = SimpleWiki() 130 | #browser.panel.Open(url) 131 | app.MainLoop() 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /psp2py/ABOUT: -------------------------------------------------------------------------------- 1 | Write something about this app. 2 | Developed with web2py. -------------------------------------------------------------------------------- /psp2py/LICENSE: -------------------------------------------------------------------------------- 1 | The web2py welcome app is licensed under public domain 2 | (except for the css and js files that it includes, which have their own third party licenses). 3 | 4 | You can modify this license when you add your own code. 5 | -------------------------------------------------------------------------------- /psp2py/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/psp2py/__init__.py -------------------------------------------------------------------------------- /psp2py/controllers/default.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # this file is released under public domain and you can use without limitations 3 | 4 | ######################################################################### 5 | ## This is a samples controller 6 | ## - index is the default action of any application 7 | ## - user is required for authentication and authorization 8 | ## - download is for downloading files uploaded in the db (does streaming) 9 | ## - call exposes all registered services (none by default) 10 | ######################################################################### 11 | 12 | def index(): 13 | """ 14 | example action using the internationalization operator T and flash 15 | rendered by views/default/index.html or views/generic.html 16 | """ 17 | return dict(message=T('Hello World')) 18 | 19 | def user(): 20 | """ 21 | exposes: 22 | http://..../[app]/default/user/login 23 | http://..../[app]/default/user/logout 24 | http://..../[app]/default/user/register 25 | http://..../[app]/default/user/profile 26 | http://..../[app]/default/user/retrieve_password 27 | http://..../[app]/default/user/change_password 28 | use @auth.requires_login() 29 | @auth.requires_membership('group name') 30 | @auth.requires_permission('read','table name',record_id) 31 | to decorate functions that need access control 32 | """ 33 | return dict(form=auth()) 34 | 35 | 36 | def download(): 37 | """ 38 | allows downloading of uploaded files 39 | http://..../[app]/default/download/[filename] 40 | """ 41 | return response.download(request,db) 42 | 43 | 44 | def call(): 45 | """ 46 | exposes services. for example: 47 | http://..../[app]/default/call/jsonrpc 48 | decorate with @services.jsonrpc the functions to expose 49 | supports xml, json, xmlrpc, jsonrpc, amfrpc, rss, csv 50 | """ 51 | return service() 52 | 53 | 54 | @auth.requires_signature() 55 | def data(): 56 | """ 57 | http://..../[app]/default/data/tables 58 | http://..../[app]/default/data/create/[table] 59 | http://..../[app]/default/data/read/[table]/[id] 60 | http://..../[app]/default/data/update/[table]/[id] 61 | http://..../[app]/default/data/delete/[table]/[id[ 62 | http://..../[app]/default/data/select/[table] 63 | http://..../[app]/default/data/search/[table] 64 | but URLs bust be signed, i.e. linked with 65 | A('table',_href=URL('data/tables',user_signature=True)) 66 | or with the signed load operator 67 | LOAD('default','data.load',args='tables',ajax=True,user_signature=True) 68 | """ 69 | return dict(form=crud()) 70 | -------------------------------------------------------------------------------- /psp2py/controllers/projects.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | # try something like 3 | 4 | def index(): 5 | return dict(form=crud.select(db.psp_project, linkto='show')) 6 | 7 | def search(): 8 | form, table=crud.search(db.psp_project, linkto='edit') 9 | return dict(form=form, table=table) 10 | 11 | def create(): 12 | return dict(form=crud.create(db.psp_project)) 13 | 14 | def show(): 15 | project_id = request.args[1] 16 | project = db(db.psp_project.id==project_id).select().first() 17 | times = db(db.psp_time_summary.project_id==project_id).select( 18 | db.psp_time_summary.phase, 19 | db.psp_time_summary.plan, 20 | db.psp_time_summary.actual, 21 | db.psp_time_summary.interruption, 22 | db.psp_time_summary.off_task) 23 | times = times.sort(lambda x: PSP_PHASES.index(x.phase)) 24 | defects = db(db.psp_defect.project_id==project_id).select( 25 | db.psp_defect.number, 26 | db.psp_defect.summary, 27 | db.psp_defect.type, 28 | db.psp_defect.inject_phase, 29 | db.psp_defect.remove_phase, 30 | db.psp_defect.fix_time, 31 | db.psp_defect.fix_defect, 32 | orderby=db.psp_defect.number, 33 | ) 34 | form = crud.read(db.psp_project, project_id) 35 | return dict(project=project, form=form, times=times, defects=defects) 36 | 37 | def edit(): 38 | return dict(form=crud.update(db.psp_project, request.args[1])) 39 | -------------------------------------------------------------------------------- /psp2py/controllers/services.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | # try something like 3 | 4 | from gluon.tools import Service 5 | service = Service(globals()) 6 | 7 | def call(): 8 | session.forget() 9 | return service() 10 | 11 | response.generic_patterns = ['*.json', '*.html'] 12 | 13 | def get_project_id(project_name): 14 | "Find the project and create a new one if it doesn't exists" 15 | project = db(db.psp_project.name==project_name).select().first() 16 | if project: 17 | project_id = project.project_id 18 | else: 19 | project_id = db.psp_project.insert(name=project_name) 20 | return project_id 21 | 22 | @service.jsonrpc 23 | def get_projects(): 24 | projects = db(db.psp_project.project_id>0).select() 25 | return [project.name for project in projects] 26 | 27 | @service.jsonrpc 28 | def save_project(project_name, defects, time_summaries, comments): 29 | project_id = get_project_id(project_name) 30 | 31 | # clean and store defects: 32 | db(db.psp_defect.project_id==project_id).delete() 33 | for defect in defects: 34 | defect['project_id'] = project_id 35 | defect.pop("id", None) 36 | defect.pop("defect_id", None) 37 | # JSON seems adding time ("2014-11-12 00:00:00"), remove it 38 | if ' ' in defect['date']: 39 | defect['date'] = defect['date'].split(' ')[0] 40 | db.psp_defect.insert(**defect) 41 | 42 | # clean and store time summaries: 43 | db(db.psp_time_summary.project_id==project_id).delete() 44 | for time_summary in time_summaries: 45 | time_summary['project_id'] = project_id 46 | if 'id' in time_summary: 47 | del time_summary['id'] 48 | db.psp_time_summary.insert(**time_summary) 49 | 50 | # clean and store comments: 51 | db(db.psp_comment.project_id==project_id).delete() 52 | for comment in comments: 53 | comment['project_id'] = project_id 54 | if 'id' in comment: 55 | del comment['id'] 56 | db.psp_comment.insert(**comment) 57 | 58 | return True 59 | 60 | @service.jsonrpc 61 | def load_project(project_name): 62 | project_id = get_project_id(project_name) 63 | defects = db(db.psp_defect.project_id==project_id).select() 64 | time_summaries = db(db.psp_time_summary.project_id==project_id).select() 65 | comments = db(db.psp_comment.project_id==project_id).select() 66 | return defects, time_summaries, comments 67 | 68 | 69 | @service.jsonrpc 70 | def update_project(project_name, actual_loc, reuse_library_entries): 71 | "Update counted LOC and reuse library entries (postmortem)" 72 | 73 | project_id = get_project_id(project_name) 74 | 75 | # update total loc counted: 76 | if project: 77 | db(db.psp_project.project_id==project_id).update(actual_loc=actual_loc) 78 | 79 | # clean and store reuse library entries: 80 | if project: 81 | db(db.psp_reuse_library.project_id==project_id).delete() 82 | for entry in reuse_library_entries: 83 | entry['project_id'] = project_id 84 | db.psp_reuse_library.insert(**entry) 85 | 86 | return True 87 | -------------------------------------------------------------------------------- /psp2py/controllers/tests.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | 3 | from statistics import calc_correlation, calc_significance, calc_linear_regression, calc_student_t_probability 4 | 5 | # test TABLE A12 [HUMPHREY95] p.514 6 | x_values = [186, 699, 132, 272, 291, 331, 199, 1890, 788, 1601] 7 | y_values = [15.0, 69.9, 6.5, 22.4, 28.4, 65.9, 19.4,198.7, 38.8, 138.2] 8 | 9 | def correlation(): 10 | r = calc_correlation(x_values, y_values) 11 | return {'r2': r**2, 'ok': round(r**2, 4)==0.9107} 12 | 13 | def linear_regression(): 14 | b0, b1 = calc_linear_regression(x_values, y_values) 15 | return {'b0': b0, 'b1': b1, 'ok': round(b0,3)==-0.351 and round(b1,3)==0.095} 16 | 17 | def significance(): 18 | # [HUMPHREY95] p.515 19 | t, r2, n = calc_significance(x_values, y_values) 20 | p = calc_student_t_probability(t, n-1) 21 | return {'loc': x_values, 'hours': y_values, 'n': n, 'r2': r2, 't': t, 'ok': round(t, 4)==9.0335, 'p': p} 22 | -------------------------------------------------------------------------------- /psp2py/controllers/webservices.py: -------------------------------------------------------------------------------- 1 | from gluon.admin import * 2 | from gluon.fileutils import abspath, read_file, write_file 3 | from gluon.tools import Service 4 | from glob import glob 5 | import shutil 6 | import platform 7 | import time 8 | import base64 9 | 10 | 11 | service = Service(globals()) 12 | 13 | def requires_admin(action): 14 | "decorator that prevents access to action if not admin password" 15 | 16 | def f(*a, **b): 17 | 18 | basic = request.env.http_authorization 19 | if not basic or not basic[:6].lower() == 'basic ': 20 | raise HTTP(401,"Wrong credentials") 21 | (username, password) = base64.b64decode(basic[6:]).split(':') 22 | if not verify_password(password) or not is_manager(): 23 | time.sleep(10) 24 | raise HTTP(403,"Not authorized") 25 | return action(*a, **b) 26 | 27 | f.__doc__ = action.__doc__ 28 | f.__name__ = action.__name__ 29 | f.__dict__.update(action.__dict__) 30 | return f 31 | 32 | 33 | 34 | @service.jsonrpc 35 | @requires_admin 36 | def login(): 37 | "dummy function to test credentials" 38 | return True 39 | 40 | 41 | @service.jsonrpc 42 | @requires_admin 43 | def list_apps(): 44 | "list installed applications" 45 | regex = re.compile('^\w+$') 46 | apps = [f for f in os.listdir(apath(r=request)) if regex.match(f)] 47 | return apps 48 | 49 | 50 | @service.jsonrpc 51 | @requires_admin 52 | def list_apps(): 53 | "list installed applications" 54 | regex = re.compile('^\w+$') 55 | apps = [f for f in os.listdir(apath(r=request)) if regex.match(f)] 56 | return apps 57 | 58 | @service.jsonrpc 59 | @requires_admin 60 | def list_files(app): 61 | files = listdir(apath('%s/' % app, r=request), '.*\.py$') 62 | return [x.replace('\\','/') for x in files] 63 | 64 | @service.jsonrpc 65 | @requires_admin 66 | def read_file(filename): 67 | """ Visualize object code """ 68 | f = open(apath(filename, r=request), "rb") 69 | try: 70 | data = f.read().replace('\r','') 71 | finally: 72 | f.close() 73 | return data 74 | 75 | @service.jsonrpc 76 | @requires_admin 77 | def write_file(filename, data): 78 | f = open(apath(filename, r=request), "wb") 79 | try: 80 | f.write(data.replace('\r\n', '\n').strip() + '\n') 81 | finally: 82 | f.close() 83 | 84 | 85 | def call(): 86 | session.forget() 87 | return service() 88 | 89 | -------------------------------------------------------------------------------- /psp2py/controllers/wiki.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | 3 | # simple wiki functionalities 4 | 5 | def index(): 6 | "List all pages" 7 | rows = db(db.wiki.id>0).select(db.wiki.page, db.wiki.title) 8 | return dict(rows=rows) 9 | 10 | def view(): 11 | "Show a page" 12 | if not request.args: 13 | page = 'index' 14 | else: 15 | page = '/'.join(request.args) 16 | 17 | rows = db(db.wiki.page==page).select() 18 | 19 | if rows: 20 | text = MARKMIN(rows[0].text) 21 | title = rows[0].title 22 | else: 23 | text = T('page not found!') 24 | title = page 25 | 26 | return dict(text=text, title=title) 27 | 28 | def load(): 29 | "Show basic html view for GUI IDE" 30 | return view() 31 | 32 | def edit(): 33 | "Edit/Create a page" 34 | if request.args: 35 | page = '/'.join(request.args) 36 | rows = db(db.wiki.page==page).select() 37 | else: 38 | rows = None 39 | page = "" 40 | 41 | if rows: 42 | form = SQLFORM(db.wiki, rows[0]) 43 | else: 44 | form = SQLFORM(db.wiki) 45 | form.vars.page = page 46 | 47 | if form.accepts(request.vars, session): 48 | session.flash = "Page updated!" 49 | redirect(URL("view", args=request.args)) 50 | 51 | return dict(form=form) 52 | -------------------------------------------------------------------------------- /psp2py/cron/crontab: -------------------------------------------------------------------------------- 1 | #crontab -------------------------------------------------------------------------------- /psp2py/languages/hu-hu.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | { 3 | '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN', 4 | '%Y-%m-%d': '%Y.%m.%d.', 5 | '%Y-%m-%d %H:%M:%S': '%Y.%m.%d. %H:%M:%S', 6 | '%s rows deleted': '%s sorok t\xc3\xb6rl\xc5\x91dtek', 7 | '%s rows updated': '%s sorok friss\xc3\xadt\xc5\x91dtek', 8 | 'Available databases and tables': 'El\xc3\xa9rhet\xc5\x91 adatb\xc3\xa1zisok \xc3\xa9s t\xc3\xa1bl\xc3\xa1k', 9 | 'Cannot be empty': 'Nem lehet \xc3\xbcres', 10 | 'Check to delete': 'T\xc3\xb6rl\xc3\xa9shez v\xc3\xa1laszd ki', 11 | 'Client IP': 'Client IP', 12 | 'Controller': 'Controller', 13 | 'Copyright': 'Copyright', 14 | 'Current request': 'Jelenlegi lek\xc3\xa9rdez\xc3\xa9s', 15 | 'Current response': 'Jelenlegi v\xc3\xa1lasz', 16 | 'Current session': 'Jelenlegi folyamat', 17 | 'DB Model': 'DB Model', 18 | 'Database': 'Adatb\xc3\xa1zis', 19 | 'Delete:': 'T\xc3\xb6r\xc3\xb6l:', 20 | 'Description': 'Description', 21 | 'E-mail': 'E-mail', 22 | 'Edit': 'Szerkeszt', 23 | 'Edit This App': 'Alkalmaz\xc3\xa1st szerkeszt', 24 | 'Edit current record': 'Aktu\xc3\xa1lis bejegyz\xc3\xa9s szerkeszt\xc3\xa9se', 25 | 'First name': 'First name', 26 | 'Group ID': 'Group ID', 27 | 'Hello World': 'Hello Vil\xc3\xa1g', 28 | 'Import/Export': 'Import/Export', 29 | 'Index': 'Index', 30 | 'Internal State': 'Internal State', 31 | 'Invalid Query': 'Hib\xc3\xa1s lek\xc3\xa9rdez\xc3\xa9s', 32 | 'Invalid email': 'Invalid email', 33 | 'Last name': 'Last name', 34 | 'Layout': 'Szerkezet', 35 | 'Main Menu': 'F\xc5\x91men\xc3\xbc', 36 | 'Menu Model': 'Men\xc3\xbc model', 37 | 'Name': 'Name', 38 | 'New Record': '\xc3\x9aj bejegyz\xc3\xa9s', 39 | 'No databases in this application': 'Nincs adatb\xc3\xa1zis ebben az alkalmaz\xc3\xa1sban', 40 | 'Origin': 'Origin', 41 | 'Password': 'Password', 42 | 'Powered by': 'Powered by', 43 | 'Query:': 'Lek\xc3\xa9rdez\xc3\xa9s:', 44 | 'Record ID': 'Record ID', 45 | 'Registration key': 'Registration key', 46 | 'Reset Password key': 'Reset Password key', 47 | 'Role': 'Role', 48 | 'Rows in table': 'Sorok a t\xc3\xa1bl\xc3\xa1ban', 49 | 'Rows selected': 'Kiv\xc3\xa1lasztott sorok', 50 | 'Stylesheet': 'Stylesheet', 51 | 'Sure you want to delete this object?': 'Biztos t\xc3\xb6rli ezt az objektumot?', 52 | 'Table name': 'Table name', 53 | 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.', 54 | 'Timestamp': 'Timestamp', 55 | 'Update:': 'Friss\xc3\xadt:', 56 | 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.', 57 | 'User ID': 'User ID', 58 | 'View': 'N\xc3\xa9zet', 59 | 'Welcome %s': 'Welcome %s', 60 | 'Welcome to web2py': 'Isten hozott a web2py-ban', 61 | 'appadmin is disabled because insecure channel': 'az appadmin a biztons\xc3\xa1gtalan csatorna miatt letiltva', 62 | 'cache': 'gyors\xc3\xadt\xc3\xb3t\xc3\xa1r', 63 | 'change password': 'jelsz\xc3\xb3 megv\xc3\xa1ltoztat\xc3\xa1sa', 64 | 'Online examples': 'online p\xc3\xa9ld\xc3\xa1k\xc3\xa9rt kattints ide', 65 | 'Administrative interface': 'az adminisztr\xc3\xa1ci\xc3\xb3s fel\xc3\xbclet\xc3\xa9rt kattints ide', 66 | 'customize me!': 'v\xc3\xa1ltoztass meg!', 67 | 'data uploaded': 'adat felt\xc3\xb6ltve', 68 | 'database': 'adatb\xc3\xa1zis', 69 | 'database %s select': 'adatb\xc3\xa1zis %s kiv\xc3\xa1laszt\xc3\xa1s', 70 | 'db': 'db', 71 | 'design': 'design', 72 | 'done!': 'k\xc3\xa9sz!', 73 | 'edit profile': 'profil szerkeszt\xc3\xa9se', 74 | 'export as csv file': 'export\xc3\xa1l csv f\xc3\xa1jlba', 75 | 'insert new': '\xc3\xbaj beilleszt\xc3\xa9se', 76 | 'insert new %s': '\xc3\xbaj beilleszt\xc3\xa9se %s', 77 | 'invalid request': 'hib\xc3\xa1s k\xc3\xa9r\xc3\xa9s', 78 | 'login': 'bel\xc3\xa9p', 79 | 'logout': 'kil\xc3\xa9p', 80 | 'lost password': 'elveszett jelsz\xc3\xb3', 81 | 'new record inserted': '\xc3\xbaj bejegyz\xc3\xa9s felv\xc3\xa9ve', 82 | 'next 100 rows': 'k\xc3\xb6vetkez\xc5\x91 100 sor', 83 | 'or import from csv file': 'vagy bet\xc3\xb6lt\xc3\xa9s csv f\xc3\xa1jlb\xc3\xb3l', 84 | 'previous 100 rows': 'el\xc5\x91z\xc5\x91 100 sor', 85 | 'record': 'bejegyz\xc3\xa9s', 86 | 'record does not exist': 'bejegyz\xc3\xa9s nem l\xc3\xa9tezik', 87 | 'record id': 'bejegyz\xc3\xa9s id', 88 | 'register': 'regisztr\xc3\xa1ci\xc3\xb3', 89 | 'selected': 'kiv\xc3\xa1lasztott', 90 | 'state': '\xc3\xa1llapot', 91 | 'table': 't\xc3\xa1bla', 92 | 'unable to parse csv file': 'nem lehet a csv f\xc3\xa1jlt beolvasni', 93 | } 94 | -------------------------------------------------------------------------------- /psp2py/languages/hu.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | { 3 | '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN', 4 | '%Y-%m-%d': '%Y.%m.%d.', 5 | '%Y-%m-%d %H:%M:%S': '%Y.%m.%d. %H:%M:%S', 6 | '%s rows deleted': '%s sorok t\xc3\xb6rl\xc5\x91dtek', 7 | '%s rows updated': '%s sorok friss\xc3\xadt\xc5\x91dtek', 8 | 'Available databases and tables': 'El\xc3\xa9rhet\xc5\x91 adatb\xc3\xa1zisok \xc3\xa9s t\xc3\xa1bl\xc3\xa1k', 9 | 'Cannot be empty': 'Nem lehet \xc3\xbcres', 10 | 'Check to delete': 'T\xc3\xb6rl\xc3\xa9shez v\xc3\xa1laszd ki', 11 | 'Client IP': 'Client IP', 12 | 'Controller': 'Controller', 13 | 'Copyright': 'Copyright', 14 | 'Current request': 'Jelenlegi lek\xc3\xa9rdez\xc3\xa9s', 15 | 'Current response': 'Jelenlegi v\xc3\xa1lasz', 16 | 'Current session': 'Jelenlegi folyamat', 17 | 'DB Model': 'DB Model', 18 | 'Database': 'Adatb\xc3\xa1zis', 19 | 'Delete:': 'T\xc3\xb6r\xc3\xb6l:', 20 | 'Description': 'Description', 21 | 'E-mail': 'E-mail', 22 | 'Edit': 'Szerkeszt', 23 | 'Edit This App': 'Alkalmaz\xc3\xa1st szerkeszt', 24 | 'Edit current record': 'Aktu\xc3\xa1lis bejegyz\xc3\xa9s szerkeszt\xc3\xa9se', 25 | 'First name': 'First name', 26 | 'Group ID': 'Group ID', 27 | 'Hello World': 'Hello Vil\xc3\xa1g', 28 | 'Import/Export': 'Import/Export', 29 | 'Index': 'Index', 30 | 'Internal State': 'Internal State', 31 | 'Invalid Query': 'Hib\xc3\xa1s lek\xc3\xa9rdez\xc3\xa9s', 32 | 'Invalid email': 'Invalid email', 33 | 'Last name': 'Last name', 34 | 'Layout': 'Szerkezet', 35 | 'Main Menu': 'F\xc5\x91men\xc3\xbc', 36 | 'Menu Model': 'Men\xc3\xbc model', 37 | 'Name': 'Name', 38 | 'New Record': '\xc3\x9aj bejegyz\xc3\xa9s', 39 | 'No databases in this application': 'Nincs adatb\xc3\xa1zis ebben az alkalmaz\xc3\xa1sban', 40 | 'Origin': 'Origin', 41 | 'Password': 'Password', 42 | 'Powered by': 'Powered by', 43 | 'Query:': 'Lek\xc3\xa9rdez\xc3\xa9s:', 44 | 'Record ID': 'Record ID', 45 | 'Registration key': 'Registration key', 46 | 'Reset Password key': 'Reset Password key', 47 | 'Role': 'Role', 48 | 'Rows in table': 'Sorok a t\xc3\xa1bl\xc3\xa1ban', 49 | 'Rows selected': 'Kiv\xc3\xa1lasztott sorok', 50 | 'Stylesheet': 'Stylesheet', 51 | 'Sure you want to delete this object?': 'Biztos t\xc3\xb6rli ezt az objektumot?', 52 | 'Table name': 'Table name', 53 | 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.', 54 | 'Timestamp': 'Timestamp', 55 | 'Update:': 'Friss\xc3\xadt:', 56 | 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.', 57 | 'User ID': 'User ID', 58 | 'View': 'N\xc3\xa9zet', 59 | 'Welcome %s': 'Welcome %s', 60 | 'Welcome to web2py': 'Isten hozott a web2py-ban', 61 | 'appadmin is disabled because insecure channel': 'az appadmin a biztons\xc3\xa1gtalan csatorna miatt letiltva', 62 | 'cache': 'gyors\xc3\xadt\xc3\xb3t\xc3\xa1r', 63 | 'change password': 'jelsz\xc3\xb3 megv\xc3\xa1ltoztat\xc3\xa1sa', 64 | 'Online examples': 'online p\xc3\xa9ld\xc3\xa1k\xc3\xa9rt kattints ide', 65 | 'Administrative interface': 'az adminisztr\xc3\xa1ci\xc3\xb3s fel\xc3\xbclet\xc3\xa9rt kattints ide', 66 | 'customize me!': 'v\xc3\xa1ltoztass meg!', 67 | 'data uploaded': 'adat felt\xc3\xb6ltve', 68 | 'database': 'adatb\xc3\xa1zis', 69 | 'database %s select': 'adatb\xc3\xa1zis %s kiv\xc3\xa1laszt\xc3\xa1s', 70 | 'db': 'db', 71 | 'design': 'design', 72 | 'done!': 'k\xc3\xa9sz!', 73 | 'edit profile': 'profil szerkeszt\xc3\xa9se', 74 | 'export as csv file': 'export\xc3\xa1l csv f\xc3\xa1jlba', 75 | 'insert new': '\xc3\xbaj beilleszt\xc3\xa9se', 76 | 'insert new %s': '\xc3\xbaj beilleszt\xc3\xa9se %s', 77 | 'invalid request': 'hib\xc3\xa1s k\xc3\xa9r\xc3\xa9s', 78 | 'login': 'bel\xc3\xa9p', 79 | 'logout': 'kil\xc3\xa9p', 80 | 'lost password': 'elveszett jelsz\xc3\xb3', 81 | 'new record inserted': '\xc3\xbaj bejegyz\xc3\xa9s felv\xc3\xa9ve', 82 | 'next 100 rows': 'k\xc3\xb6vetkez\xc5\x91 100 sor', 83 | 'or import from csv file': 'vagy bet\xc3\xb6lt\xc3\xa9s csv f\xc3\xa1jlb\xc3\xb3l', 84 | 'previous 100 rows': 'el\xc5\x91z\xc5\x91 100 sor', 85 | 'record': 'bejegyz\xc3\xa9s', 86 | 'record does not exist': 'bejegyz\xc3\xa9s nem l\xc3\xa9tezik', 87 | 'record id': 'bejegyz\xc3\xa9s id', 88 | 'register': 'regisztr\xc3\xa1ci\xc3\xb3', 89 | 'selected': 'kiv\xc3\xa1lasztott', 90 | 'state': '\xc3\xa1llapot', 91 | 'table': 't\xc3\xa1bla', 92 | 'unable to parse csv file': 'nem lehet a csv f\xc3\xa1jlt beolvasni', 93 | } 94 | -------------------------------------------------------------------------------- /psp2py/languages/it-it.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | { 3 | '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" è un\'espressione opzionale come "campo1=\'nuovo valore\'". Non si può fare "update" o "delete" dei risultati di un JOIN ', 4 | '%Y-%m-%d': '%d/%m/%Y', 5 | '%Y-%m-%d %H:%M:%S': '%d/%m/%Y %H:%M:%S', 6 | '%s rows deleted': '%s righe ("record") cancellate', 7 | '%s rows updated': '%s righe ("record") modificate', 8 | 'Available databases and tables': 'Database e tabelle disponibili', 9 | 'Cannot be empty': 'Non può essere vuoto', 10 | 'Check to delete': 'Seleziona per cancellare', 11 | 'Client IP': 'Client IP', 12 | 'Controller': 'Controller', 13 | 'Copyright': 'Copyright', 14 | 'Current request': 'Richiesta (request) corrente', 15 | 'Current response': 'Risposta (response) corrente', 16 | 'Current session': 'Sessione (session) corrente', 17 | 'DB Model': 'Modello di DB', 18 | 'Database': 'Database', 19 | 'Delete:': 'Cancella:', 20 | 'Description': 'Descrizione', 21 | 'E-mail': 'E-mail', 22 | 'Edit': 'Modifica', 23 | 'Edit This App': 'Modifica questa applicazione', 24 | 'Edit current record': 'Modifica record corrente', 25 | 'First name': 'Nome', 26 | 'Group ID': 'ID Gruppo', 27 | 'Hello World': 'Salve Mondo', 28 | 'Hello World in a flash!': 'Salve Mondo in un flash!', 29 | 'Import/Export': 'Importa/Esporta', 30 | 'Index': 'Indice', 31 | 'Internal State': 'Stato interno', 32 | 'Invalid Query': 'Richiesta (query) non valida', 33 | 'Invalid email': 'Email non valida', 34 | 'Last name': 'Cognome', 35 | 'Layout': 'Layout', 36 | 'Main Menu': 'Menu principale', 37 | 'Menu Model': 'Menu Modelli', 38 | 'Name': 'Nome', 39 | 'New Record': 'Nuovo elemento (record)', 40 | 'No databases in this application': 'Nessun database presente in questa applicazione', 41 | 'Origin': 'Origine', 42 | 'Password': 'Password', 43 | 'Powered by': 'Powered by', 44 | 'Query:': 'Richiesta (query):', 45 | 'Record ID': 'Record ID', 46 | 'Registration key': 'Chiave di Registazione', 47 | 'Reset Password key': 'Resetta chiave Password ', 48 | 'Role': 'Ruolo', 49 | 'Rows in table': 'Righe nella tabella', 50 | 'Rows selected': 'Righe selezionate', 51 | 'Stylesheet': 'Foglio di stile (stylesheet)', 52 | 'Sure you want to delete this object?': 'Vuoi veramente cancellare questo oggetto?', 53 | 'Table name': 'Nome tabella', 54 | 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'La richiesta (query) è una condizione come ad esempio "db.tabella1.campo1==\'valore\'". Una condizione come "db.tabella1.campo1==db.tabella2.campo2" produce un "JOIN" SQL.', 55 | 'The output of the file is a dictionary that was rendered by the view': 'L\'output del file è un "dictionary" che è stato visualizzato dalla vista', 56 | 'This is a copy of the scaffolding application': "Questa è una copia dell'applicazione di base (scaffold)", 57 | 'Timestamp': 'Ora (timestamp)', 58 | 'Update:': 'Aggiorna:', 59 | 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Per costruire richieste (query) più complesse si usano (...)&(...) come "e" (AND), (...)|(...) come "o" (OR), e ~(...) come negazione (NOT).', 60 | 'User ID': 'ID Utente', 61 | 'View': 'Vista', 62 | 'Welcome %s': 'Benvenuto %s', 63 | 'Welcome to web2py': 'Benvenuto su web2py', 64 | 'Which called the function': 'che ha chiamato la funzione', 65 | 'You are successfully running web2py': 'Stai eseguendo web2py con successo', 66 | 'You can modify this application and adapt it to your needs': 'Puoi modificare questa applicazione adattandola alle tue necessità', 67 | 'You visited the url': "Hai visitato l'URL", 68 | 'appadmin is disabled because insecure channel': 'Amministrazione (appadmin) disabilitata: comunicazione non sicura', 69 | 'cache': 'cache', 70 | 'change password': 'Cambia password', 71 | 'Online examples': 'Vedere gli esempi', 72 | 'Administrative interface': "Interfaccia amministrativa", 73 | 'customize me!': 'Personalizzami!', 74 | 'data uploaded': 'dati caricati', 75 | 'database': 'database', 76 | 'database %s select': 'database %s select', 77 | 'db': 'db', 78 | 'design': 'progetta', 79 | 'Documentation': 'Documentazione', 80 | 'done!': 'fatto!', 81 | 'edit profile': 'modifica profilo', 82 | 'export as csv file': 'esporta come file CSV', 83 | 'hello world': 'salve mondo', 84 | 'insert new': 'inserisci nuovo', 85 | 'insert new %s': 'inserisci nuovo %s', 86 | 'invalid request': 'richiesta non valida', 87 | 'located in the file': 'presente nel file', 88 | 'login': 'accesso', 89 | 'logout': 'uscita', 90 | 'lost password?': 'dimenticato la password?', 91 | 'new record inserted': 'nuovo record inserito', 92 | 'next 100 rows': 'prossime 100 righe', 93 | 'not authorized': 'non autorizzato', 94 | 'or import from csv file': 'oppure importa da file CSV', 95 | 'previous 100 rows': '100 righe precedenti', 96 | 'record': 'record', 97 | 'record does not exist': 'il record non esiste', 98 | 'record id': 'record id', 99 | 'register': 'registrazione', 100 | 'selected': 'selezionato', 101 | 'state': 'stato', 102 | 'table': 'tabella', 103 | 'unable to parse csv file': 'non riesco a decodificare questo file CSV', 104 | } 105 | -------------------------------------------------------------------------------- /psp2py/languages/it.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | { 3 | '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" è un\'espressione opzionale come "campo1=\'nuovo valore\'". Non si può fare "update" o "delete" dei risultati di un JOIN ', 4 | '%Y-%m-%d': '%d/%m/%Y', 5 | '%Y-%m-%d %H:%M:%S': '%d/%m/%Y %H:%M:%S', 6 | '%s rows deleted': '%s righe ("record") cancellate', 7 | '%s rows updated': '%s righe ("record") modificate', 8 | 'Available databases and tables': 'Database e tabelle disponibili', 9 | 'Cannot be empty': 'Non può essere vuoto', 10 | 'Check to delete': 'Seleziona per cancellare', 11 | 'Client IP': 'Client IP', 12 | 'Controller': 'Controller', 13 | 'Copyright': 'Copyright', 14 | 'Current request': 'Richiesta (request) corrente', 15 | 'Current response': 'Risposta (response) corrente', 16 | 'Current session': 'Sessione (session) corrente', 17 | 'DB Model': 'Modello di DB', 18 | 'Database': 'Database', 19 | 'Delete:': 'Cancella:', 20 | 'Description': 'Descrizione', 21 | 'E-mail': 'E-mail', 22 | 'Edit': 'Modifica', 23 | 'Edit This App': 'Modifica questa applicazione', 24 | 'Edit current record': 'Modifica record corrente', 25 | 'First name': 'Nome', 26 | 'Group ID': 'ID Gruppo', 27 | 'Hello World': 'Salve Mondo', 28 | 'Hello World in a flash!': 'Salve Mondo in un flash!', 29 | 'Import/Export': 'Importa/Esporta', 30 | 'Index': 'Indice', 31 | 'Internal State': 'Stato interno', 32 | 'Invalid Query': 'Richiesta (query) non valida', 33 | 'Invalid email': 'Email non valida', 34 | 'Last name': 'Cognome', 35 | 'Layout': 'Layout', 36 | 'Main Menu': 'Menu principale', 37 | 'Menu Model': 'Menu Modelli', 38 | 'Name': 'Nome', 39 | 'New Record': 'Nuovo elemento (record)', 40 | 'No databases in this application': 'Nessun database presente in questa applicazione', 41 | 'Origin': 'Origine', 42 | 'Password': 'Password', 43 | 'Powered by': 'Powered by', 44 | 'Query:': 'Richiesta (query):', 45 | 'Record ID': 'Record ID', 46 | 'Registration key': 'Chiave di Registazione', 47 | 'Reset Password key': 'Resetta chiave Password ', 48 | 'Role': 'Ruolo', 49 | 'Rows in table': 'Righe nella tabella', 50 | 'Rows selected': 'Righe selezionate', 51 | 'Stylesheet': 'Foglio di stile (stylesheet)', 52 | 'Sure you want to delete this object?': 'Vuoi veramente cancellare questo oggetto?', 53 | 'Table name': 'Nome tabella', 54 | 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'La richiesta (query) è una condizione come ad esempio "db.tabella1.campo1==\'valore\'". Una condizione come "db.tabella1.campo1==db.tabella2.campo2" produce un "JOIN" SQL.', 55 | 'The output of the file is a dictionary that was rendered by the view': 'L\'output del file è un "dictionary" che è stato visualizzato dalla vista', 56 | 'This is a copy of the scaffolding application': "Questa è una copia dell'applicazione di base (scaffold)", 57 | 'Timestamp': 'Ora (timestamp)', 58 | 'Update:': 'Aggiorna:', 59 | 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Per costruire richieste (query) più complesse si usano (...)&(...) come "e" (AND), (...)|(...) come "o" (OR), e ~(...) come negazione (NOT).', 60 | 'User ID': 'ID Utente', 61 | 'View': 'Vista', 62 | 'Welcome %s': 'Benvenuto %s', 63 | 'Welcome to web2py': 'Benvenuto su web2py', 64 | 'Which called the function': 'che ha chiamato la funzione', 65 | 'You are successfully running web2py': 'Stai eseguendo web2py con successo', 66 | 'You can modify this application and adapt it to your needs': 'Puoi modificare questa applicazione adattandola alle tue necessità', 67 | 'You visited the url': "Hai visitato l'URL", 68 | 'appadmin is disabled because insecure channel': 'Amministrazione (appadmin) disabilitata: comunicazione non sicura', 69 | 'cache': 'cache', 70 | 'change password': 'Cambia password', 71 | 'Online examples': 'Vedere gli esempi', 72 | 'Administrative interface': "Interfaccia amministrativa", 73 | 'customize me!': 'Personalizzami!', 74 | 'data uploaded': 'dati caricati', 75 | 'database': 'database', 76 | 'database %s select': 'database %s select', 77 | 'db': 'db', 78 | 'design': 'progetta', 79 | 'Documentation': 'Documentazione', 80 | 'done!': 'fatto!', 81 | 'edit profile': 'modifica profilo', 82 | 'export as csv file': 'esporta come file CSV', 83 | 'hello world': 'salve mondo', 84 | 'insert new': 'inserisci nuovo', 85 | 'insert new %s': 'inserisci nuovo %s', 86 | 'invalid request': 'richiesta non valida', 87 | 'located in the file': 'presente nel file', 88 | 'login': 'accesso', 89 | 'logout': 'uscita', 90 | 'lost password?': 'dimenticato la password?', 91 | 'new record inserted': 'nuovo record inserito', 92 | 'next 100 rows': 'prossime 100 righe', 93 | 'not authorized': 'non autorizzato', 94 | 'or import from csv file': 'oppure importa da file CSV', 95 | 'previous 100 rows': '100 righe precedenti', 96 | 'record': 'record', 97 | 'record does not exist': 'il record non esiste', 98 | 'record id': 'record id', 99 | 'register': 'registrazione', 100 | 'selected': 'selezionato', 101 | 'state': 'stato', 102 | 'table': 'tabella', 103 | 'unable to parse csv file': 'non riesco a decodificare questo file CSV', 104 | } 105 | -------------------------------------------------------------------------------- /psp2py/languages/pl-pl.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | { 3 | '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"Uaktualnij" jest dodatkowym wyra\xc5\xbceniem postaci "pole1=\'nowawarto\xc5\x9b\xc4\x87\'". Nie mo\xc5\xbcesz uaktualni\xc4\x87 lub usun\xc4\x85\xc4\x87 wynik\xc3\xb3w z JOIN:', 4 | '%Y-%m-%d': '%Y-%m-%d', 5 | '%Y-%m-%d %H:%M:%S': '%Y-%m-%d %H:%M:%S', 6 | '%s rows deleted': 'Wierszy usuni\xc4\x99tych: %s', 7 | '%s rows updated': 'Wierszy uaktualnionych: %s', 8 | 'Available databases and tables': 'Dost\xc4\x99pne bazy danych i tabele', 9 | 'Cannot be empty': 'Nie mo\xc5\xbce by\xc4\x87 puste', 10 | 'Change Password': 'Change Password', 11 | 'Check to delete': 'Zaznacz aby usun\xc4\x85\xc4\x87', 12 | 'Controller': 'Controller', 13 | 'Copyright': 'Copyright', 14 | 'Current request': 'Aktualne \xc5\xbc\xc4\x85danie', 15 | 'Current response': 'Aktualna odpowied\xc5\xba', 16 | 'Current session': 'Aktualna sesja', 17 | 'DB Model': 'DB Model', 18 | 'Database': 'Database', 19 | 'Delete:': 'Usu\xc5\x84:', 20 | 'Edit': 'Edit', 21 | 'Edit Profile': 'Edit Profile', 22 | 'Edit This App': 'Edit This App', 23 | 'Edit current record': 'Edytuj aktualny rekord', 24 | 'Hello World': 'Witaj \xc5\x9awiecie', 25 | 'Import/Export': 'Importuj/eksportuj', 26 | 'Index': 'Index', 27 | 'Internal State': 'Stan wewn\xc4\x99trzny', 28 | 'Invalid Query': 'B\xc5\x82\xc4\x99dne zapytanie', 29 | 'Layout': 'Layout', 30 | 'Login': 'Zaloguj', 31 | 'Logout': 'Logout', 32 | 'Lost Password': 'Przypomnij has\xc5\x82o', 33 | 'Main Menu': 'Main Menu', 34 | 'Menu Model': 'Menu Model', 35 | 'New Record': 'Nowy rekord', 36 | 'No databases in this application': 'Brak baz danych w tej aplikacji', 37 | 'Powered by': 'Powered by', 38 | 'Query:': 'Zapytanie:', 39 | 'Register': 'Zarejestruj', 40 | 'Rows in table': 'Wiersze w tabeli', 41 | 'Rows selected': 'Wybrane wiersze', 42 | 'Stylesheet': 'Stylesheet', 43 | 'Sure you want to delete this object?': 'Czy na pewno chcesz usun\xc4\x85\xc4\x87 ten obiekt?', 44 | 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': '"Zapytanie" jest warunkiem postaci "db.tabela1.pole1==\'warto\xc5\x9b\xc4\x87\'". Takie co\xc5\x9b jak "db.tabela1.pole1==db.tabela2.pole2" oznacza SQL JOIN.', 45 | 'Update:': 'Uaktualnij:', 46 | 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'U\xc5\xbcyj (...)&(...) jako AND, (...)|(...) jako OR oraz ~(...) jako NOT do tworzenia bardziej skomplikowanych zapyta\xc5\x84.', 47 | 'View': 'View', 48 | 'Welcome %s': 'Welcome %s', 49 | 'Welcome to web2py': 'Witaj w web2py', 50 | 'appadmin is disabled because insecure channel': 'appadmin is disabled because insecure channel', 51 | 'cache': 'cache', 52 | 'change password': 'change password', 53 | 'Online examples': 'Kliknij aby przej\xc5\x9b\xc4\x87 do interaktywnych przyk\xc5\x82ad\xc3\xb3w', 54 | 'Administrative interface': 'Kliknij aby przej\xc5\x9b\xc4\x87 do panelu administracyjnego', 55 | 'customize me!': 'dostosuj mnie!', 56 | 'data uploaded': 'dane wys\xc5\x82ane', 57 | 'database': 'baza danych', 58 | 'database %s select': 'wyb\xc3\xb3r z bazy danych %s', 59 | 'db': 'baza danych', 60 | 'design': 'projektuj', 61 | 'done!': 'zrobione!', 62 | 'edit profile': 'edit profile', 63 | 'export as csv file': 'eksportuj jako plik csv', 64 | 'insert new': 'wstaw nowy rekord tabeli', 65 | 'insert new %s': 'wstaw nowy rekord do tabeli %s', 66 | 'invalid request': 'B\xc5\x82\xc4\x99dne \xc5\xbc\xc4\x85danie', 67 | 'login': 'login', 68 | 'logout': 'logout', 69 | 'new record inserted': 'nowy rekord zosta\xc5\x82 wstawiony', 70 | 'next 100 rows': 'nast\xc4\x99pne 100 wierszy', 71 | 'or import from csv file': 'lub zaimportuj z pliku csv', 72 | 'previous 100 rows': 'poprzednie 100 wierszy', 73 | 'record': 'record', 74 | 'record does not exist': 'rekord nie istnieje', 75 | 'record id': 'id rekordu', 76 | 'register': 'register', 77 | 'selected': 'wybranych', 78 | 'state': 'stan', 79 | 'table': 'tabela', 80 | 'unable to parse csv file': 'nie mo\xc5\xbcna sparsowa\xc4\x87 pliku csv', 81 | } 82 | -------------------------------------------------------------------------------- /psp2py/languages/pl.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | { 3 | '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"Uaktualnij" jest dodatkowym wyra\xc5\xbceniem postaci "pole1=\'nowawarto\xc5\x9b\xc4\x87\'". Nie mo\xc5\xbcesz uaktualni\xc4\x87 lub usun\xc4\x85\xc4\x87 wynik\xc3\xb3w z JOIN:', 4 | '%Y-%m-%d': '%Y-%m-%d', 5 | '%Y-%m-%d %H:%M:%S': '%Y-%m-%d %H:%M:%S', 6 | '%s rows deleted': 'Wierszy usuni\xc4\x99tych: %s', 7 | '%s rows updated': 'Wierszy uaktualnionych: %s', 8 | 'Authentication': 'Uwierzytelnienie', 9 | 'Available databases and tables': 'Dost\xc4\x99pne bazy danych i tabele', 10 | 'Cannot be empty': 'Nie mo\xc5\xbce by\xc4\x87 puste', 11 | 'Change Password': 'Zmie\xc5\x84 has\xc5\x82o', 12 | 'Check to delete': 'Zaznacz aby usun\xc4\x85\xc4\x87', 13 | 'Check to delete:': 'Zaznacz aby usun\xc4\x85\xc4\x87:', 14 | 'Client IP': 'IP klienta', 15 | 'Controller': 'Kontroler', 16 | 'Copyright': 'Copyright', 17 | 'Current request': 'Aktualne \xc5\xbc\xc4\x85danie', 18 | 'Current response': 'Aktualna odpowied\xc5\xba', 19 | 'Current session': 'Aktualna sesja', 20 | 'DB Model': 'Model bazy danych', 21 | 'Database': 'Baza danych', 22 | 'Delete:': 'Usu\xc5\x84:', 23 | 'Description': 'Opis', 24 | 'E-mail': 'Adres e-mail', 25 | 'Edit': 'Edycja', 26 | 'Edit Profile': 'Edytuj profil', 27 | 'Edit This App': 'Edytuj t\xc4\x99 aplikacj\xc4\x99', 28 | 'Edit current record': 'Edytuj obecny rekord', 29 | 'First name': 'Imi\xc4\x99', 30 | 'Function disabled': 'Funkcja wy\xc5\x82\xc4\x85czona', 31 | 'Group ID': 'ID grupy', 32 | 'Hello World': 'Witaj \xc5\x9awiecie', 33 | 'Import/Export': 'Importuj/eksportuj', 34 | 'Index': 'Indeks', 35 | 'Internal State': 'Stan wewn\xc4\x99trzny', 36 | 'Invalid Query': 'B\xc5\x82\xc4\x99dne zapytanie', 37 | 'Invalid email': 'B\xc5\x82\xc4\x99dny adres email', 38 | 'Last name': 'Nazwisko', 39 | 'Layout': 'Uk\xc5\x82ad', 40 | 'Login': 'Zaloguj', 41 | 'Logout': 'Wyloguj', 42 | 'Lost Password': 'Przypomnij has\xc5\x82o', 43 | 'Main Menu': 'Menu g\xc5\x82\xc3\xb3wne', 44 | 'Menu Model': 'Model menu', 45 | 'Name': 'Nazwa', 46 | 'New Record': 'Nowy rekord', 47 | 'No databases in this application': 'Brak baz danych w tej aplikacji', 48 | 'Origin': '\xc5\xb9r\xc3\xb3d\xc5\x82o', 49 | 'Password': 'Has\xc5\x82o', 50 | "Password fields don't match": 'Pola has\xc5\x82a nie s\xc4\x85 zgodne ze sob\xc4\x85', 51 | 'Powered by': 'Zasilane przez', 52 | 'Query:': 'Zapytanie:', 53 | 'Record ID': 'ID rekordu', 54 | 'Register': 'Zarejestruj', 55 | 'Registration key': 'Klucz rejestracji', 56 | 'Role': 'Rola', 57 | 'Rows in table': 'Wiersze w tabeli', 58 | 'Rows selected': 'Wybrane wiersze', 59 | 'Stylesheet': 'Arkusz styl\xc3\xb3w', 60 | 'Submit': 'Wy\xc5\x9blij', 61 | 'Sure you want to delete this object?': 'Czy na pewno chcesz usun\xc4\x85\xc4\x87 ten obiekt?', 62 | 'Table name': 'Nazwa tabeli', 63 | 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': '"Zapytanie" jest warunkiem postaci "db.tabela1.pole1==\'warto\xc5\x9b\xc4\x87\'". Takie co\xc5\x9b jak "db.tabela1.pole1==db.tabela2.pole2" oznacza SQL JOIN.', 64 | 'Timestamp': 'Znacznik czasu', 65 | 'Update:': 'Uaktualnij:', 66 | 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'U\xc5\xbcyj (...)&(...) jako AND, (...)|(...) jako OR oraz ~(...) jako NOT do tworzenia bardziej skomplikowanych zapyta\xc5\x84.', 67 | 'User %(id)s Registered': 'U\xc5\xbcytkownik %(id)s zosta\xc5\x82 zarejestrowany', 68 | 'User ID': 'ID u\xc5\xbcytkownika', 69 | 'Verify Password': 'Potwierd\xc5\xba has\xc5\x82o', 70 | 'View': 'Widok', 71 | 'Welcome %s': 'Welcome %s', 72 | 'Welcome to web2py': 'Witaj w web2py', 73 | 'appadmin is disabled because insecure channel': 'administracja aplikacji wy\xc5\x82\xc4\x85czona z powodu braku bezpiecznego po\xc5\x82\xc4\x85czenia', 74 | 'cache': 'cache', 75 | 'change password': 'change password', 76 | 'Online examples': 'Kliknij aby przej\xc5\x9b\xc4\x87 do interaktywnych przyk\xc5\x82ad\xc3\xb3w', 77 | 'Administrative interface': 'Kliknij aby przej\xc5\x9b\xc4\x87 do panelu administracyjnego', 78 | 'customize me!': 'dostosuj mnie!', 79 | 'data uploaded': 'dane wys\xc5\x82ane', 80 | 'database': 'baza danych', 81 | 'database %s select': 'wyb\xc3\xb3r z bazy danych %s', 82 | 'db': 'baza danych', 83 | 'design': 'projektuj', 84 | 'done!': 'zrobione!', 85 | 'edit profile': 'edit profile', 86 | 'export as csv file': 'eksportuj jako plik csv', 87 | 'insert new': 'wstaw nowy rekord tabeli', 88 | 'insert new %s': 'wstaw nowy rekord do tabeli %s', 89 | 'invalid request': 'B\xc5\x82\xc4\x99dne \xc5\xbc\xc4\x85danie', 90 | 'login': 'login', 91 | 'logout': 'logout', 92 | 'new record inserted': 'nowy rekord zosta\xc5\x82 wstawiony', 93 | 'next 100 rows': 'nast\xc4\x99pne 100 wierszy', 94 | 'or import from csv file': 'lub zaimportuj z pliku csv', 95 | 'previous 100 rows': 'poprzednie 100 wierszy', 96 | 'record': 'rekord', 97 | 'record does not exist': 'rekord nie istnieje', 98 | 'record id': 'id rekordu', 99 | 'register': 'register', 100 | 'selected': 'wybranych', 101 | 'state': 'stan', 102 | 'table': 'tabela', 103 | 'unable to parse csv file': 'nie mo\xc5\xbcna sparsowa\xc4\x87 pliku csv', 104 | } 105 | -------------------------------------------------------------------------------- /psp2py/languages/pt-pt.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | { 3 | '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" é uma expressão opcional como "field1=\'newvalue\'". Não pode actualizar ou eliminar os resultados de um JOIN', 4 | '%Y-%m-%d': '%Y-%m-%d', 5 | '%Y-%m-%d %H:%M:%S': '%Y-%m-%d %H:%M:%S', 6 | '%s rows deleted': '%s linhas eliminadas', 7 | '%s rows updated': '%s linhas actualizadas', 8 | 'About': 'About', 9 | 'Author Reference Auth User': 'Author Reference Auth User', 10 | 'Author Reference Auth User.username': 'Author Reference Auth User.username', 11 | 'Available databases and tables': 'bases de dados e tabelas disponíveis', 12 | 'Cannot be empty': 'não pode ser vazio', 13 | 'Category Create': 'Category Create', 14 | 'Category Select': 'Category Select', 15 | 'Check to delete': 'seleccione para eliminar', 16 | 'Comment Create': 'Comment Create', 17 | 'Comment Select': 'Comment Select', 18 | 'Content': 'Content', 19 | 'Controller': 'Controlador', 20 | 'Copyright': 'Direitos de cópia', 21 | 'Created By': 'Created By', 22 | 'Created On': 'Created On', 23 | 'Current request': 'pedido currente', 24 | 'Current response': 'resposta currente', 25 | 'Current session': 'sessão currente', 26 | 'DB Model': 'Modelo de BD', 27 | 'Database': 'Base de dados', 28 | 'Delete:': 'Eliminar:', 29 | 'Edit': 'Editar', 30 | 'Edit This App': 'Edite esta aplicação', 31 | 'Edit current record': 'Edição de registo currente', 32 | 'Email': 'Email', 33 | 'First Name': 'First Name', 34 | 'For %s #%s': 'For %s #%s', 35 | 'Hello World': 'Olá Mundo', 36 | 'Import/Export': 'Importar/Exportar', 37 | 'Index': 'Índice', 38 | 'Internal State': 'Estado interno', 39 | 'Invalid Query': 'Consulta Inválida', 40 | 'Last Name': 'Last Name', 41 | 'Layout': 'Esboço', 42 | 'Main Menu': 'Menu Principal', 43 | 'Menu Model': 'Menu do Modelo', 44 | 'Modified By': 'Modified By', 45 | 'Modified On': 'Modified On', 46 | 'Name': 'Name', 47 | 'New Record': 'Novo Registo', 48 | 'No Data': 'No Data', 49 | 'No databases in this application': 'Não há bases de dados nesta aplicação', 50 | 'Password': 'Password', 51 | 'Post Create': 'Post Create', 52 | 'Post Select': 'Post Select', 53 | 'Powered by': 'Suportado por', 54 | 'Query:': 'Interrogação:', 55 | 'Replyto Reference Post': 'Replyto Reference Post', 56 | 'Rows in table': 'Linhas numa tabela', 57 | 'Rows selected': 'Linhas seleccionadas', 58 | 'Stylesheet': 'Folha de estilo', 59 | 'Sure you want to delete this object?': 'Tem a certeza que deseja eliminar este objecto?', 60 | 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'A "query" é uma condição do tipo "db.table1.field1==\'value\'". Algo como "db.table1.field1==db.table2.field2" resultaria num SQL JOIN.', 61 | 'Title': 'Title', 62 | 'Update:': 'Actualização:', 63 | 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Utilize (...)&(...) para AND, (...)|(...) para OR, e ~(...) para NOT para construir interrogações mais complexas.', 64 | 'Username': 'Username', 65 | 'View': 'Vista', 66 | 'Welcome %s': 'Bem-vindo(a) %s', 67 | 'Welcome to Gluonization': 'Bem vindo ao Web2py', 68 | 'Welcome to web2py': 'Bem-vindo(a) ao web2py', 69 | 'When': 'When', 70 | 'appadmin is disabled because insecure channel': 'appadmin está desactivada pois o canal é inseguro', 71 | 'cache': 'cache', 72 | 'change password': 'alterar palavra-chave', 73 | 'Online examples': 'Exemplos online', 74 | 'Administrative interface': 'Painel administrativo', 75 | 'create new category': 'create new category', 76 | 'create new comment': 'create new comment', 77 | 'create new post': 'create new post', 78 | 'customize me!': 'Personaliza-me!', 79 | 'data uploaded': 'informação enviada', 80 | 'database': 'base de dados', 81 | 'database %s select': 'selecção de base de dados %s', 82 | 'db': 'bd', 83 | 'design': 'design', 84 | 'done!': 'concluído!', 85 | 'edit category': 'edit category', 86 | 'edit comment': 'edit comment', 87 | 'edit post': 'edit post', 88 | 'edit profile': 'Editar perfil', 89 | 'export as csv file': 'exportar como ficheiro csv', 90 | 'insert new': 'inserir novo', 91 | 'insert new %s': 'inserir novo %s', 92 | 'invalid request': 'Pedido Inválido', 93 | 'login': 'login', 94 | 'logout': 'logout', 95 | 'new record inserted': 'novo registo inserido', 96 | 'next 100 rows': 'próximas 100 linhas', 97 | 'or import from csv file': 'ou importe a partir de ficheiro csv', 98 | 'previous 100 rows': '100 linhas anteriores', 99 | 'record': 'registo', 100 | 'record does not exist': 'registo inexistente', 101 | 'record id': 'id de registo', 102 | 'register': 'register', 103 | 'search category': 'search category', 104 | 'search comment': 'search comment', 105 | 'search post': 'search post', 106 | 'select category': 'select category', 107 | 'select comment': 'select comment', 108 | 'select post': 'select post', 109 | 'selected': 'seleccionado(s)', 110 | 'show category': 'show category', 111 | 'show comment': 'show comment', 112 | 'show post': 'show post', 113 | 'state': 'estado', 114 | 'table': 'tabela', 115 | 'unable to parse csv file': 'não foi possível carregar ficheiro csv', 116 | } 117 | -------------------------------------------------------------------------------- /psp2py/languages/pt.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | { 3 | '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" é uma expressão opcional como "field1=\'newvalue\'". Não pode actualizar ou eliminar os resultados de um JOIN', 4 | '%Y-%m-%d': '%Y-%m-%d', 5 | '%Y-%m-%d %H:%M:%S': '%Y-%m-%d %H:%M:%S', 6 | '%s rows deleted': '%s linhas eliminadas', 7 | '%s rows updated': '%s linhas actualizadas', 8 | 'About': 'About', 9 | 'Author Reference Auth User': 'Author Reference Auth User', 10 | 'Author Reference Auth User.username': 'Author Reference Auth User.username', 11 | 'Available databases and tables': 'bases de dados e tabelas disponíveis', 12 | 'Cannot be empty': 'não pode ser vazio', 13 | 'Category Create': 'Category Create', 14 | 'Category Select': 'Category Select', 15 | 'Check to delete': 'seleccione para eliminar', 16 | 'Comment Create': 'Comment Create', 17 | 'Comment Select': 'Comment Select', 18 | 'Content': 'Content', 19 | 'Controller': 'Controlador', 20 | 'Copyright': 'Direitos de cópia', 21 | 'Created By': 'Created By', 22 | 'Created On': 'Created On', 23 | 'Current request': 'pedido currente', 24 | 'Current response': 'resposta currente', 25 | 'Current session': 'sessão currente', 26 | 'DB Model': 'Modelo de BD', 27 | 'Database': 'Base de dados', 28 | 'Delete:': 'Eliminar:', 29 | 'Edit': 'Editar', 30 | 'Edit This App': 'Edite esta aplicação', 31 | 'Edit current record': 'Edição de registo currente', 32 | 'Email': 'Email', 33 | 'First Name': 'First Name', 34 | 'For %s #%s': 'For %s #%s', 35 | 'Hello World': 'Olá Mundo', 36 | 'Import/Export': 'Importar/Exportar', 37 | 'Index': 'Índice', 38 | 'Internal State': 'Estado interno', 39 | 'Invalid Query': 'Consulta Inválida', 40 | 'Last Name': 'Last Name', 41 | 'Layout': 'Esboço', 42 | 'Main Menu': 'Menu Principal', 43 | 'Menu Model': 'Menu do Modelo', 44 | 'Modified By': 'Modified By', 45 | 'Modified On': 'Modified On', 46 | 'Name': 'Name', 47 | 'New Record': 'Novo Registo', 48 | 'No Data': 'No Data', 49 | 'No databases in this application': 'Não há bases de dados nesta aplicação', 50 | 'Password': 'Password', 51 | 'Post Create': 'Post Create', 52 | 'Post Select': 'Post Select', 53 | 'Powered by': 'Suportado por', 54 | 'Query:': 'Interrogação:', 55 | 'Replyto Reference Post': 'Replyto Reference Post', 56 | 'Rows in table': 'Linhas numa tabela', 57 | 'Rows selected': 'Linhas seleccionadas', 58 | 'Stylesheet': 'Folha de estilo', 59 | 'Sure you want to delete this object?': 'Tem a certeza que deseja eliminar este objecto?', 60 | 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'A "query" é uma condição do tipo "db.table1.field1==\'value\'". Algo como "db.table1.field1==db.table2.field2" resultaria num SQL JOIN.', 61 | 'Title': 'Title', 62 | 'Update:': 'Actualização:', 63 | 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Utilize (...)&(...) para AND, (...)|(...) para OR, e ~(...) para NOT para construir interrogações mais complexas.', 64 | 'Username': 'Username', 65 | 'View': 'Vista', 66 | 'Welcome %s': 'Bem-vindo(a) %s', 67 | 'Welcome to Gluonization': 'Bem vindo ao Web2py', 68 | 'Welcome to web2py': 'Bem-vindo(a) ao web2py', 69 | 'When': 'When', 70 | 'appadmin is disabled because insecure channel': 'appadmin está desactivada pois o canal é inseguro', 71 | 'cache': 'cache', 72 | 'change password': 'alterar palavra-chave', 73 | 'Online examples': 'Exemplos online', 74 | 'Administrative interface': 'Painel administrativo', 75 | 'create new category': 'create new category', 76 | 'create new comment': 'create new comment', 77 | 'create new post': 'create new post', 78 | 'customize me!': 'Personaliza-me!', 79 | 'data uploaded': 'informação enviada', 80 | 'database': 'base de dados', 81 | 'database %s select': 'selecção de base de dados %s', 82 | 'db': 'bd', 83 | 'design': 'design', 84 | 'done!': 'concluído!', 85 | 'edit category': 'edit category', 86 | 'edit comment': 'edit comment', 87 | 'edit post': 'edit post', 88 | 'edit profile': 'Editar perfil', 89 | 'export as csv file': 'exportar como ficheiro csv', 90 | 'insert new': 'inserir novo', 91 | 'insert new %s': 'inserir novo %s', 92 | 'invalid request': 'Pedido Inválido', 93 | 'login': 'login', 94 | 'logout': 'logout', 95 | 'new record inserted': 'novo registo inserido', 96 | 'next 100 rows': 'próximas 100 linhas', 97 | 'or import from csv file': 'ou importe a partir de ficheiro csv', 98 | 'previous 100 rows': '100 linhas anteriores', 99 | 'record': 'registo', 100 | 'record does not exist': 'registo inexistente', 101 | 'record id': 'id de registo', 102 | 'register': 'register', 103 | 'search category': 'search category', 104 | 'search comment': 'search comment', 105 | 'search post': 'search post', 106 | 'select category': 'select category', 107 | 'select comment': 'select comment', 108 | 'select post': 'select post', 109 | 'selected': 'seleccionado(s)', 110 | 'show category': 'show category', 111 | 'show comment': 'show comment', 112 | 'show post': 'show post', 113 | 'state': 'estado', 114 | 'table': 'tabela', 115 | 'unable to parse csv file': 'não foi possível carregar ficheiro csv', 116 | } 117 | -------------------------------------------------------------------------------- /psp2py/languages/ru-ru.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | { 3 | '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"Изменить" - необязательное выражение вида "field1=\'новое значение\'". Результаты операции JOIN нельзя изменить или удалить.', 4 | '%Y-%m-%d': '%Y-%m-%d', 5 | '%Y-%m-%d %H:%M:%S': '%Y-%m-%d %H:%M:%S', 6 | '%s rows deleted': '%s строк удалено', 7 | '%s rows updated': '%s строк изменено', 8 | 'Available databases and tables': 'Базы данных и таблицы', 9 | 'Cannot be empty': 'Пустое значение недопустимо', 10 | 'Change Password': 'Смените пароль', 11 | 'Check to delete': 'Удалить', 12 | 'Check to delete:': 'Удалить:', 13 | 'Client IP': 'Client IP', 14 | 'Current request': 'Текущий запрос', 15 | 'Current response': 'Текущий ответ', 16 | 'Current session': 'Текущая сессия', 17 | 'Delete:': 'Удалить:', 18 | 'Description': 'Описание', 19 | 'E-mail': 'E-mail', 20 | 'Edit Profile': 'Редактировать профиль', 21 | 'Edit current record': 'Редактировать текущую запись', 22 | 'First name': 'Имя', 23 | 'Group ID': 'Group ID', 24 | 'Hello World': 'Заработало!', 25 | 'Import/Export': 'Импорт/экспорт', 26 | 'Internal State': 'Внутренне состояние', 27 | 'Invalid Query': 'Неверный запрос', 28 | 'Invalid email': 'Неверный email', 29 | 'Invalid login': 'Неверный логин', 30 | 'Invalid password': 'Неверный пароль', 31 | 'Last name': 'Фамилия', 32 | 'Logged in': 'Вход выполнен', 33 | 'Logged out': 'Выход выполнен', 34 | 'Login': 'Вход', 35 | 'Logout': 'Выход', 36 | 'Lost Password': 'Забыли пароль?', 37 | 'Name': 'Name', 38 | 'New Record': 'Новая запись', 39 | 'New password': 'Новый пароль', 40 | 'No databases in this application': 'В приложении нет баз данных', 41 | 'Old password': 'Старый пароль', 42 | 'Origin': 'Происхождение', 43 | 'Password': 'Пароль', 44 | "Password fields don't match": 'Пароли не совпадают', 45 | 'Query:': 'Запрос:', 46 | 'Record ID': 'ID записи', 47 | 'Register': 'Зарегистрироваться', 48 | 'Registration key': 'Ключ регистрации', 49 | 'Remember me (for 30 days)': 'Запомнить меня (на 30 дней)', 50 | 'Reset Password key': 'Сбросить ключ пароля', 51 | 'Role': 'Роль', 52 | 'Rows in table': 'Строк в таблице', 53 | 'Rows selected': 'Выделено строк', 54 | 'Submit': 'Отправить', 55 | 'Sure you want to delete this object?': 'Подтвердите удаление объекта', 56 | 'Table name': 'Имя таблицы', 57 | 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': '"Запрос" - это условие вида "db.table1.field1==\'значение\'". Выражение вида "db.table1.field1==db.table2.field2" формирует SQL JOIN.', 58 | 'Timestamp': 'Отметка времени', 59 | 'Update:': 'Изменить:', 60 | 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Для построение сложных запросов используйте операторы "И": (...)&(...), "ИЛИ": (...)|(...), "НЕ": ~(...).', 61 | 'User %(id)s Logged-in': 'Пользователь %(id)s вошёл', 62 | 'User %(id)s Logged-out': 'Пользователь %(id)s вышел', 63 | 'User %(id)s Password changed': 'Пользователь %(id)s сменил пароль', 64 | 'User %(id)s Profile updated': 'Пользователь %(id)s обновил профиль', 65 | 'User %(id)s Registered': 'Пользователь %(id)s зарегистрировался', 66 | 'User ID': 'ID пользователя', 67 | 'Verify Password': 'Повторите пароль', 68 | 'Welcome to web2py': 'Добро пожаловать в web2py', 69 | 'Online examples': 'примеры он-лайн', 70 | 'Administrative interface': 'административный интерфейс', 71 | 'customize me!': 'настройте внешний вид!', 72 | 'data uploaded': 'данные загружены', 73 | 'database': 'база данных', 74 | 'database %s select': 'выбор базы данных %s', 75 | 'db': 'БД', 76 | 'design': 'дизайн', 77 | 'done!': 'готово!', 78 | 'export as csv file': 'экспорт в csv-файл', 79 | 'insert new': 'добавить', 80 | 'insert new %s': 'добавить %s', 81 | 'invalid request': 'неверный запрос', 82 | 'login': 'вход', 83 | 'logout': 'выход', 84 | 'new record inserted': 'новая запись добавлена', 85 | 'next 100 rows': 'следующие 100 строк', 86 | 'or import from csv file': 'или импорт из csv-файла', 87 | 'password': 'пароль', 88 | 'previous 100 rows': 'предыдущие 100 строк', 89 | 'profile': 'профиль', 90 | 'record does not exist': 'запись не найдена', 91 | 'record id': 'id записи', 92 | 'selected': 'выбрано', 93 | 'state': 'состояние', 94 | 'table': 'таблица', 95 | 'unable to parse csv file': 'нечитаемый csv-файл', 96 | } 97 | -------------------------------------------------------------------------------- /psp2py/languages/sk-sk.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | { 3 | '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" je voliteľný výraz ako "field1=\'newvalue\'". Nemôžete upravovať alebo zmazať výsledky JOINu', 4 | '%Y-%m-%d': '%d.%m.%Y', 5 | '%Y-%m-%d %H:%M:%S': '%d.%m.%Y %H:%M:%S', 6 | '%s rows deleted': '%s zmazaných záznamov', 7 | '%s rows updated': '%s upravených záznamov', 8 | 'Available databases and tables': 'Dostupné databázy a tabuľky', 9 | 'Cannot be empty': 'Nemôže byť prázdne', 10 | 'Check to delete': 'Označiť na zmazanie', 11 | 'Controller': 'Controller', 12 | 'Copyright': 'Copyright', 13 | 'Current request': 'Aktuálna požiadavka', 14 | 'Current response': 'Aktuálna odpoveď', 15 | 'Current session': 'Aktuálne sedenie', 16 | 'DB Model': 'DB Model', 17 | 'Database': 'Databáza', 18 | 'Delete:': 'Zmazať:', 19 | 'Description': 'Popis', 20 | 'Edit': 'Upraviť', 21 | 'Edit Profile': 'Upraviť profil', 22 | 'Edit current record': 'Upraviť aktuálny záznam', 23 | 'First name': 'Krstné meno', 24 | 'Group ID': 'ID skupiny', 25 | 'Hello World': 'Ahoj svet', 26 | 'Import/Export': 'Import/Export', 27 | 'Index': 'Index', 28 | 'Internal State': 'Vnútorný stav', 29 | 'Invalid email': 'Neplatný email', 30 | 'Invalid Query': 'Neplatná otázka', 31 | 'Invalid password': 'Nesprávne heslo', 32 | 'Last name': 'Priezvisko', 33 | 'Layout': 'Layout', 34 | 'Logged in': 'Prihlásený', 35 | 'Logged out': 'Odhlásený', 36 | 'Lost Password': 'Stratené heslo?', 37 | 'Menu Model': 'Menu Model', 38 | 'Name': 'Meno', 39 | 'New Record': 'Nový záznam', 40 | 'New password': 'Nové heslo', 41 | 'No databases in this application': 'V tejto aplikácii nie sú databázy', 42 | 'Old password': 'Staré heslo', 43 | 'Origin': 'Pôvod', 44 | 'Password': 'Heslo', 45 | 'Powered by': 'Powered by', 46 | 'Query:': 'Otázka:', 47 | 'Record ID': 'ID záznamu', 48 | 'Register': 'Zaregistrovať sa', 49 | 'Registration key': 'Registračný kľúč', 50 | 'Remember me (for 30 days)': 'Zapamätaj si ma (na 30 dní)', 51 | 'Reset Password key': 'Nastaviť registračný kľúč', 52 | 'Role': 'Rola', 53 | 'Rows in table': 'riadkov v tabuľke', 54 | 'Rows selected': 'označených riadkov', 55 | 'Submit': 'Odoslať', 56 | 'Stylesheet': 'Stylesheet', 57 | 'Sure you want to delete this object?': 'Ste si istí, že chcete zmazať tento objekt?', 58 | 'Table name': 'Názov tabuľky', 59 | 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': '"query" je podmienka ako "db.table1.field1==\'value\'". Niečo ako "db.table1.field1==db.table2.field2" má za výsledok SQL JOIN.', 60 | 'The output of the file is a dictionary that was rendered by the view': 'Výstup zo súboru je slovník, ktorý bol zobrazený vo view', 61 | 'This is a copy of the scaffolding application': 'Toto je kópia skeletu aplikácie', 62 | 'Timestamp': 'Časová pečiatka', 63 | 'Update:': 'Upraviť:', 64 | 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Použite (...)&(...) pre AND, (...)|(...) pre OR a ~(...) pre NOT na poskladanie komplexnejších otázok.', 65 | 'User %(id)s Logged-in': 'Používateľ %(id)s prihlásený', 66 | 'User %(id)s Logged-out': 'Používateľ %(id)s odhlásený', 67 | 'User %(id)s Password changed': 'Používateľ %(id)s zmenil heslo', 68 | 'User %(id)s Profile updated': 'Používateľ %(id)s upravil profil', 69 | 'User %(id)s Registered': 'Používateľ %(id)s sa zaregistroval', 70 | 'User ID': 'ID používateľa', 71 | 'Verify Password': 'Zopakujte heslo', 72 | 'View': 'Zobraziť', 73 | 'Welcome to web2py': 'Vitajte vo web2py', 74 | 'Which called the function': 'Ktorý zavolal funkciu', 75 | 'You are successfully running web2py': 'Úspešne ste spustili web2py', 76 | 'You can modify this application and adapt it to your needs': 'Môžete upraviť túto aplikáciu a prispôsobiť ju svojim potrebám', 77 | 'You visited the url': 'Navštívili ste URL', 78 | 'appadmin is disabled because insecure channel': 'appadmin je zakázaný bez zabezpečeného spojenia', 79 | 'cache': 'cache', 80 | 'Online examples': 'pre online príklady kliknite sem', 81 | 'Administrative interface': 'pre administrátorské rozhranie kliknite sem', 82 | 'customize me!': 'prispôsob ma!', 83 | 'data uploaded': 'údaje naplnené', 84 | 'database': 'databáza', 85 | 'database %s select': 'databáza %s výber', 86 | 'db': 'db', 87 | 'design': 'návrh', 88 | 'Documentation': 'Dokumentácia', 89 | 'done!': 'hotovo!', 90 | 'export as csv file': 'exportovať do csv súboru', 91 | 'insert new': 'vložiť nový záznam ', 92 | 'insert new %s': 'vložiť nový záznam %s', 93 | 'invalid request': 'Neplatná požiadavka', 94 | 'located in the file': 'nachádzajúci sa v súbore ', 95 | 'login': 'prihlásiť', 96 | 'logout': 'odhlásiť', 97 | 'lost password?': 'stratené heslo?', 98 | 'new record inserted': 'nový záznam bol vložený', 99 | 'next 100 rows': 'ďalších 100 riadkov', 100 | 'or import from csv file': 'alebo naimportovať z csv súboru', 101 | 'password': 'heslo', 102 | 'previous 100 rows': 'predchádzajúcich 100 riadkov', 103 | 'record': 'záznam', 104 | 'record does not exist': 'záznam neexistuje', 105 | 'record id': 'id záznamu', 106 | 'register': 'registrovať', 107 | 'selected': 'označených', 108 | 'state': 'stav', 109 | 'table': 'tabuľka', 110 | 'unable to parse csv file': 'nedá sa načítať csv súbor', 111 | } 112 | -------------------------------------------------------------------------------- /psp2py/models/db.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # this file is released under public domain and you can use without limitations 3 | 4 | ######################################################################### 5 | ## This scaffolding model makes your app work on Google App Engine too 6 | ######################################################################### 7 | 8 | if request.env.web2py_runtime_gae: # if running on Google App Engine 9 | db = DAL('google:datastore') # connect to Google BigTable 10 | # optional DAL('gae://namespace') 11 | session.connect(request, response, db = db) # and store sessions and tickets there 12 | ### or use the following lines to store sessions in Memcache 13 | # from gluon.contrib.memdb import MEMDB 14 | # from google.appengine.api.memcache import Client 15 | # session.connect(request, response, db = MEMDB(Client())) 16 | else: # else use a normal relational database 17 | db = DAL('sqlite://storage.sqlite') # if not, use SQLite or other DB 18 | 19 | # by default give a view/generic.extension to all actions from localhost 20 | # none otherwise. a pattern can be 'controller/function.extension' 21 | response.generic_patterns = ['*'] if request.is_local else [] 22 | 23 | ######################################################################### 24 | ## Here is sample code if you need for 25 | ## - email capabilities 26 | ## - authentication (registration, login, logout, ... ) 27 | ## - authorization (role based authorization) 28 | ## - services (xml, csv, json, xmlrpc, jsonrpc, amf, rss) 29 | ## - crud actions 30 | ## (more options discussed in gluon/tools.py) 31 | ######################################################################### 32 | 33 | from gluon.tools import Mail, Auth, Crud, Service, PluginManager, prettydate 34 | mail = Mail() # mailer 35 | auth = Auth(db) # authentication/authorization 36 | crud = Crud(db) # for CRUD helpers using auth 37 | service = Service() # for json, xml, jsonrpc, xmlrpc, amfrpc 38 | plugins = PluginManager() # for configuring plugins 39 | 40 | mail.settings.server = 'logging' or 'smtp.gmail.com:587' # your SMTP server 41 | mail.settings.sender = 'you@gmail.com' # your email 42 | mail.settings.login = 'username:password' # your credentials or None 43 | 44 | auth.settings.hmac_key = 'sha512:89b9d005-19d8-47d3-b182-271fd9c7db8e' # before define_tables() 45 | auth.define_tables() # creates all needed tables 46 | auth.settings.mailer = mail # for user email verification 47 | auth.settings.registration_requires_verification = False 48 | auth.settings.registration_requires_approval = False 49 | auth.messages.verify_email = 'Click on the link http://'+request.env.http_host+URL('default','user',args=['verify_email'])+'/%(key)s to verify your email' 50 | auth.settings.reset_password_requires_verification = True 51 | auth.messages.reset_password = 'Click on the link http://'+request.env.http_host+URL('default','user',args=['reset_password'])+'/%(key)s to reset your password' 52 | 53 | ######################################################################### 54 | ## If you need to use OpenID, Facebook, MySpace, Twitter, Linkedin, etc. 55 | ## register with janrain.com, uncomment and customize following 56 | # from gluon.contrib.login_methods.rpx_account import RPXAccount 57 | # auth.settings.actions_disabled = \ 58 | # ['register','change_password','request_reset_password'] 59 | # auth.settings.login_form = RPXAccount(request, api_key='...',domain='...', 60 | # url = "http://localhost:8000/%s/default/user/login" % request.application) 61 | ## other login methods are in gluon/contrib/login_methods 62 | ######################################################################### 63 | 64 | crud.settings.auth = None # =auth to enforce authorization on crud 65 | 66 | ######################################################################### 67 | ## Define your tables below (or better in another model file) for example 68 | ## 69 | ## >>> db.define_table('mytable',Field('myfield','string')) 70 | ## 71 | ## Fields can be 'string','text','password','integer','double','boolean' 72 | ## 'date','time','datetime','blob','upload', 'reference TABLENAME' 73 | ## There is an implicit 'id integer autoincrement' field 74 | ## Consult manual for more options, validators, etc. 75 | ## 76 | ## More API examples for controllers: 77 | ## 78 | ## >>> db.mytable.insert(myfield='value') 79 | ## >>> rows=db(db.mytable.myfield=='value').select(db.mytable.ALL) 80 | ## >>> for row in rows: print row.id, row.myfield 81 | ######################################################################### 82 | -------------------------------------------------------------------------------- /psp2py/models/db_psp.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | 3 | PSP_PHASES = ["planning", "design", "code", "review", "compile", "test", "postmortem"] 4 | PSP_TIMES = ["plan", "actual", "interruption"] 5 | 6 | PSP_DEFECT_TYPES = {10: 'Documentation', 20: 'Syntax', 30: 'Coding standard', 7 | 40: 'Assignment', 50: 'Interface', 60: 'Checking', 70: 'Data', 8 | 80: 'Function', 90: 'System', 100: 'Enviroment'} 9 | 10 | # function/class type classification for easier reuse and estimation: 11 | PSP_CATEGORIES = ["module", "model", "controller", "view"] 12 | PSP_SIZES = ["very small", "small", "medium", "large", "very large"] 13 | 14 | # Personal Software Process tables: 15 | 16 | db.define_table("psp_project", 17 | Field("project_id", "id"), 18 | Field("name", "string"), 19 | Field("description", "string"), 20 | Field("requeriments", "text"), 21 | Field("testing", "text"), 22 | Field("user_id", db.auth_user), 23 | #Field("instructor_id", db.auth_user), 24 | Field("started", "date"), 25 | Field("completed", "date"), 26 | Field("planned_loc", "integer", comment="Total new & changed (estimated program size)"), 27 | Field("actual_loc", "integer", comment="Total new & changed (measured program size)"), 28 | Field("planned_time", "double", 29 | comment=T("Original projected development time")), 30 | Field("time_lpi", "double", label=T("Time LPI"), 31 | comment="Total Time Lower Prediction Interval"), 32 | Field("time_upi", "double", label=T("Time UPI"), 33 | comment="Total Time Upper Prediction Interval"), 34 | format="%(name)s", 35 | ) 36 | 37 | 38 | db.define_table("psp_time_summary", 39 | Field("id", "id"), 40 | Field("project_id", db.psp_project), 41 | Field("phase", "string", requires=IS_IN_SET(PSP_PHASES)), 42 | Field("plan", "integer"), 43 | Field("actual", "integer"), 44 | Field("off_task", "integer"), 45 | Field("interruption", "integer"), 46 | ) 47 | 48 | db.define_table("psp_comment", 49 | Field("id", "id"), 50 | Field("project_id", db.psp_project), 51 | Field("phase", "string", requires=IS_IN_SET(PSP_PHASES)), 52 | Field("message", "text"), 53 | Field("delta", "integer"), 54 | ) 55 | 56 | db.define_table("psp_defect", 57 | Field("id", "id"), 58 | Field("project_id", db.psp_project), 59 | Field("number", "integer"), 60 | Field("summary", "text"), 61 | Field("description", "text"), 62 | Field("date", "date"), 63 | Field("type", "string", requires=IS_IN_SET(PSP_DEFECT_TYPES)), 64 | Field("inject_phase", "string", requires=IS_IN_SET(PSP_PHASES)), 65 | Field("remove_phase", "string", requires=IS_IN_SET(PSP_PHASES)), 66 | Field("fix_time", "integer"), 67 | Field("fix_defect", "integer"), 68 | Field("filename", "string"), 69 | Field("lineno", "integer"), 70 | Field("offset", "integer"), 71 | Field("uuid", "string"), 72 | ) 73 | 74 | 75 | def pretty_time(counter): 76 | "return formatted string of a time count in seconds (days/hours/min/seg)" 77 | # find time unit and convert to it 78 | if counter is None: 79 | return "" 80 | counter = int(counter) 81 | for factor, unit in ((1., 's'), (60., 'm'), (3600., 'h')): 82 | if counter < (60 * factor): 83 | break 84 | # only print fraction if it is not an integer result 85 | if counter % factor: 86 | return "%0.2f %s" % (counter/factor, unit) 87 | else: 88 | return "%d %s" % (counter/factor, unit) 89 | 90 | db.psp_time_summary.plan.represent = pretty_time 91 | db.psp_time_summary.actual.represent = pretty_time 92 | db.psp_time_summary.interruption.represent = pretty_time 93 | db.psp_time_summary.off_task.represent = pretty_time 94 | db.psp_defect.fix_time.represent = pretty_time 95 | db.psp_project.planned_time.represent = lambda x: x and ("%0.2f hs" % x) or '' 96 | db.psp_project.time_upi.represent = lambda x: x and ("%0.2f hs" % x) or '' 97 | db.psp_project.time_lpi.represent = lambda x: x and ("%0.2f hs" % x) or '' 98 | 99 | 100 | db.define_table("psp_reuse_library", 101 | Field("id", "id"), 102 | Field("project_id", db.psp_project), 103 | Field("filename", "string"), 104 | Field("class_name", "string"), 105 | Field("function_name", "string"), 106 | Field("category", "string", 107 | requires=IS_IN_SET(PSP_CATEGORIES), 108 | default="module"), 109 | Field("lineno", "integer"), 110 | Field("loc", "integer"), 111 | ) 112 | -------------------------------------------------------------------------------- /psp2py/models/db_wiki.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | 3 | db.define_table("wiki", 4 | Field("page", requires=IS_NOT_EMPTY()), 5 | Field("title", requires=IS_NOT_EMPTY()), 6 | Field("text", "text", 7 | requires=IS_NOT_EMPTY(), 8 | comment=XML(A(str(T('MARKMIN format')),_href='http://web2py.com/examples/static/markmin.html')), 9 | ), 10 | ) 11 | -------------------------------------------------------------------------------- /psp2py/models/menu.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # this file is released under public domain and you can use without limitations 3 | ######################################################################### 4 | ## Customize your APP title, subtitle and menus here 5 | ######################################################################### 6 | 7 | response.title = request.application 8 | response.subtitle = T('customize me!') 9 | 10 | #http://dev.w3.org/html5/markup/meta.name.html 11 | response.meta.author = 'Mariano Reingart' 12 | response.meta.description = 'Personal Software Process support webapp' 13 | response.meta.keywords = 'web2py, python, framework, psp' 14 | response.meta.generator = 'Web2py Enterprise Framework' 15 | response.meta.copyright = 'Copyright 2011' 16 | 17 | 18 | ########################################## 19 | ## this is the main application menu 20 | ## add/remove items as required 21 | ########################################## 22 | 23 | response.menu = [ 24 | (T('Home'), False, URL('default','index'), []), 25 | (T('Projects'), False, URL('projects','index'), [ 26 | (T('Search'), False, URL('projects','search'), []), 27 | (T('Create'), False, URL('projects','create'), []), 28 | ]), 29 | (T('PROBE'), False, URL('probe','index'), [ 30 | (T('Categorize'), False, URL('probe','categorize'), []), 31 | (T('Reuse Library'), False, URL('probe','library'), []), 32 | (T('Size Log-Normal Distribution'), False, URL('probe','normal_distribution.png'), []), 33 | ]), 34 | (T('Estimate'), False, URL('estimate','index'), [ 35 | (T('Correlation'), False, URL('estimate','correlation'), []), 36 | (T('Significance'), False, URL('estimate','significance'), []), 37 | (T('Time in phase'), False, URL('estimate','time_in_phase'), []), 38 | (T('Size vs Time Linear Regression'), False, URL('estimate','linear_regression.png'), []), 39 | ]), 40 | (T('Reports'), False, URL('reports','index'), [ 41 | (T('Projects'), False, URL('reports','projects'), []), 42 | (T('Defects'), False, URL('reports','defects'), [ 43 | (T('Pareto Distribution'), False, URL('reports','pareto_distribution.png'), []), 44 | (T('Average Fix Times'), False, URL('reports','average_fix_time.png'), []), 45 | ]), 46 | ]), 47 | (T('Wiki'), False, URL('wiki','index'), []), 48 | ] 49 | -------------------------------------------------------------------------------- /psp2py/modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/psp2py/modules/__init__.py -------------------------------------------------------------------------------- /psp2py/modules/draws.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | def draw_linear_regression(x, y, x_label, y_label, title, body): 4 | "Plot a linear regression chart" 5 | # x and y are matplotlib pylab arrays, body is a StringIO 6 | import pylab 7 | import matplotlib 8 | # clear graph 9 | matplotlib.pyplot.clf() 10 | matplotlib.use('Agg') 11 | #nse = 0.3 * pylab.randn(len(x)) 12 | #y = 2 + 3 * x + nse 13 | # the best fit line from polyfit ; you can do arbitrary order 14 | # polynomials but here we take advantage of a line being a first order 15 | # polynomial 16 | m, b = pylab.polyfit( x , y , 1 ) 17 | # plot the data with blue circles and the best fit with a thick 18 | # solid black line 19 | pylab.plot(x, y, 'bo ', x, m * x+b , '-k' , linewidth=2) 20 | pylab.ylabel(y_label) 21 | pylab.xlabel(x_label) 22 | pylab.grid(True) 23 | pylab.title(title) 24 | pylab.savefig(body) 25 | return body.getvalue() 26 | 27 | 28 | def draw_normal_histogram(x, bins, y_label='', x_label='', title="", body=""): 29 | "Plot a histogram chart" 30 | # x are matplotlib pylab arrays, body is a StringIO 31 | import pylab 32 | import matplotlib 33 | # clear graph 34 | matplotlib.pyplot.clf() 35 | matplotlib.use('Agg') 36 | n, bins1, patches = pylab.hist(x, bins, histtype='bar', facecolor='green', alpha=0.75) 37 | #pylab.setp(patches, 'facecolor', 'g', 'alpha', 0.75) 38 | pylab.ylabel(y_label) 39 | pylab.xlabel(x_label) 40 | # add a line showing the expected distribution 41 | mu = pylab.mean(x) 42 | sigma = pylab.std(x) 43 | y = pylab.normpdf(bins, mu, sigma) 44 | l = pylab.plot(bins, y, 'k--', linewidth=1.5) 45 | 46 | pylab.title(title) 47 | 48 | pylab.grid(True) 49 | pylab.savefig(body) 50 | return body.getvalue() 51 | 52 | 53 | def draw_barchart(values, title, y_label, x_label, x_tick_labels, autolabel=False, text="", stacked=True, body=None): 54 | #!/usr/bin/env python 55 | # a bar plot with errorbars 56 | import pylab 57 | import matplotlib 58 | import numpy as np 59 | import matplotlib.pyplot as plt 60 | 61 | 62 | rects = [] 63 | labels = [] 64 | bottom = None # for stacked bars 65 | 66 | fig = plt.figure() 67 | ax = fig.add_subplot(111) 68 | ind = np.arange(len(values[0][3])) # the x locations for the groups 69 | 70 | for i, (label, width, color, heights) in enumerate(values): 71 | labels.append(label) 72 | w = not stacked and width or 0 73 | rects.append(ax.bar(ind+w*i, heights, width, bottom=bottom, color=color)) 74 | if stacked: 75 | bottom = [x + i for x, i in zip(bottom or [0]*len(heights), heights)] 76 | 77 | # add some 78 | ax.set_ylabel(y_label) 79 | ax.set_xlabel(x_label) 80 | ax.set_title(title) 81 | ax.set_xticks(ind+width/2.) 82 | ax.set_xticklabels( x_tick_labels ) 83 | 84 | ax.legend( [r[0] for r in rects], labels, loc="best") 85 | 86 | def draw_autolabel(rects): 87 | # attach some text labels 88 | for rect in rects: 89 | height = rect.get_height() 90 | ax.text(rect.get_x()+rect.get_width()/2., 1.05*height, '%d'%int(height), 91 | ha='center', va='bottom') 92 | 93 | if autolabel: 94 | for rect in rects: 95 | draw_autolabel(rect) 96 | 97 | pylab.grid(True) 98 | 99 | if text: 100 | plt.text(7.75, 27, text, size=12, rotation=0., 101 | ha="center", va="center", 102 | bbox = dict(boxstyle="round", 103 | ec='black', 104 | fc='w', 105 | ) 106 | ) 107 | pylab.savefig(body) 108 | return body.getvalue() 109 | 110 | 111 | #Some simple functions to generate colours. 112 | def pastel(colour, weight=2.4): 113 | """ Convert colour into a nice pastel shade""" 114 | from matplotlib.colors import colorConverter 115 | import numpy as np 116 | rgb = np.asarray(colorConverter.to_rgb(colour)) 117 | # scale colour 118 | maxc = max(rgb) 119 | if maxc < 1.0 and maxc > 0: 120 | # scale colour 121 | scale = 1.0 / maxc 122 | rgb = rgb * scale 123 | # now decrease saturation 124 | total = sum(rgb) 125 | slack = 0 126 | for x in rgb: 127 | slack += 1.0 - x 128 | 129 | # want to increase weight from total to weight 130 | # pick x s.t. slack * x == weight - total 131 | # x = (weight - total) / slack 132 | x = (weight - total) / slack 133 | 134 | rgb = [c + (x * (1.0-c)) for c in rgb] 135 | 136 | return rgb 137 | 138 | def get_colours(n): 139 | """ Return n pastel colours. """ 140 | import numpy as np 141 | import matplotlib 142 | base = np.asarray([[1,0,0], [0,1,0], [0,0,1]]) 143 | 144 | if n <= 3: 145 | return base[0:n] 146 | 147 | # how many new colours to we need to insert between 148 | # red and green and between green and blue? 149 | needed = (((n - 3) + 1) / 2, (n - 3) / 2) 150 | 151 | colours = [] 152 | for start in (0, 1): 153 | for x in np.linspace(0, 1, needed[start]+2): 154 | colours.append((base[start] * (1.0 - x)) + 155 | (base[start+1] * x)) 156 | 157 | return [pastel(c) for c in colours[0:n]] 158 | -------------------------------------------------------------------------------- /psp2py/modules/integration.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from math import e, pi, sqrt 4 | 5 | 6 | def compute_integral(f, x_low, x_high, w, n): 7 | "Compute the numerical approximation of a definite integral" 8 | # composite simpson rule 9 | term1 = f(x_low) 10 | term2 = 0 11 | for j in xrange(1, n, 2): 12 | term2 += f(x_low + j * w) 13 | term3 = 0 14 | for j in xrange(2, n, 2): 15 | term3 += f(x_low + j * w) 16 | term4 = f(x_high) 17 | y = w / 3.0 * (term1 + 4 * term2 + 2 * term3 + term4) 18 | 19 | return y 20 | 21 | 22 | def simpson_rule_integrate(f, x_low, x_high, error=0.00001): 23 | # 1. identify the upper and lower limits of the numerical integration 24 | if x_high < 0 and x_low == float("-infinity"): 25 | x_high = abs(x_high) 26 | x_low = 0 27 | p = -0.5 28 | elif x_low == float("-infinity"): 29 | x_low = 0 30 | p = 0.5 31 | else: 32 | p = 0 33 | # 2. select an initial number N and old result 34 | n = 20 35 | old_y = 0 36 | while True: 37 | # 3. divide the range to get the segment width 38 | w = (x_high - x_low) / n 39 | # 4. compute the numerical integration approximation 40 | y = compute_integral(f, x_low, x_high, w, n) 41 | # 5. compare with the old result if error is permisible 42 | if abs(y - old_y) <= error: 43 | if p >= 0: 44 | return p + y 45 | else: 46 | return - p - y 47 | old_y = y 48 | # 6. double N 49 | n = 2 * n 50 | # 7. repeat ... 51 | 52 | 53 | def factorial(x, step=1): 54 | "Calculate integer or float factorial (for gamma function, step=2)" 55 | if x > step: 56 | return x * factorial(x - step, step) / step 57 | return x / step 58 | 59 | 60 | def gamma(n, d=2): 61 | "Calculate gamma function value for a fraction (numerator & denominator)" 62 | # WARNING: only tested for d=2 ! 63 | if n % 2 != 0 and d == 2: 64 | return factorial(n - 2, step=2.0) * sqrt(pi) 65 | else: 66 | i = n / d 67 | return factorial(i - 1) 68 | 69 | 70 | def f_normal_distribution(u): 71 | return (1 / (2 * pi) ** (0.5)) * e ** ((- u ** 2) / 2) 72 | 73 | 74 | def f_student_t_distribution(n): 75 | # student t distribution 76 | # n = degrees of freedom 77 | k = gamma(n + 1, 2) / (sqrt(n*pi) * gamma(n, 2)) 78 | return lambda u: k * (1 + (u ** 2) / float(n)) ** (- (n +1) / 2.0) 79 | -------------------------------------------------------------------------------- /psp2py/modules/statistics.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf8 3 | 4 | import math 5 | from integration import f_student_t_distribution, simpson_rule_integrate 6 | 7 | 8 | def mean(values): 9 | "Calculate the average of the numbers given" 10 | return sum(values) / float(len(values)) 11 | 12 | 13 | def calc_correlation(x_values, y_values): 14 | "Calculate strength of a relationship between two sets of data" 15 | # calculate aux variables 16 | n = len(x_values) 17 | sum_xy = sum([(x_values[i] * y_values[i]) for i in range(n)]) 18 | sum_x = sum([(x_values[i]) for i in range(n)]) 19 | sum_y = sum([(y_values[i]) for i in range(n)]) 20 | sum_x2 = sum([(x_values[i] ** 2) for i in range(n)]) 21 | sum_y2 = sum([(y_values[i] ** 2) for i in range(n)]) 22 | 23 | # calculate corelation 24 | r = (n * sum_xy - (sum_x * sum_y)) / math.sqrt((n * sum_x2 - sum_x ** 2) * (n * sum_y2 - sum_y ** 2)) 25 | 26 | return r 27 | 28 | 29 | def calc_significance(x_values, y_values): 30 | "Calculate the significance (likelihood of two set of data correlation)" 31 | n = len (x_values) 32 | r = calc_correlation(x_values, y_values) 33 | r2 = r**2 34 | t = abs(r)*math.sqrt(n - 2)/math.sqrt(1 - r**2) 35 | return t, r2, n 36 | 37 | 38 | def calc_linear_regression(x_values, y_values): 39 | "Calculate the linear regression parameters for a set of n values" 40 | 41 | # calculate aux variables 42 | x_avg = mean(x_values) 43 | y_avg = mean(y_values) 44 | n = len(x_values) 45 | sum_xy = sum([(x_values[i] * y_values[i]) for i in range(n)]) 46 | sum_x2 = sum([(x_values[i] ** 2) for i in range(n)]) 47 | 48 | # calculate regression coefficients 49 | b1 = (sum_xy - (n * x_avg * y_avg)) / (sum_x2 - n * (x_avg ** 2)) 50 | b0 = y_avg - b1 * x_avg 51 | 52 | return (b0, b1) 53 | 54 | 55 | def calc_standard_deviation(values): 56 | "Calculate the standard deviation of a list of number values" 57 | x_avg = mean(values) 58 | n = len(values) 59 | sd = math.sqrt(sum([(x_i - x_avg)**2 60 | for x_i in values]) / float(n)) 61 | return sd, x_avg 62 | 63 | def calc_student_t_probability(x, n): 64 | "Integrate t distribution from -infinity to x with n degrees of freedom" 65 | inf = float("infinity") 66 | p = simpson_rule_integrate(f_student_t_distribution(n), -inf, x) 67 | return p 68 | 69 | def calc_double_sided_student_t_probability(t, n): 70 | "Calculate the p-value using a double sided student t distribution" 71 | # integrate a finite area from the origin to t 72 | p_aux = simpson_rule_integrate(f_student_t_distribution(n), 0, t) 73 | # return the area of the two tails of the distribution (symmetrical) 74 | return (0.5 - p_aux) * 2 75 | 76 | def calc_double_sided_student_t_value(p, n): 77 | "Calculate the t-value using a double sided student t distribution" 78 | # replaces table lookup, thanks to http://statpages.org/pdfs.html 79 | v = dv = 0.5 80 | t = 0 81 | while dv > 0.000001: 82 | t = 1 / v - 1 83 | dv = dv / 2 84 | if calc_double_sided_student_t_probability(t, n) > p: 85 | v = v - dv 86 | else: 87 | v = v + dv 88 | return t 89 | 90 | 91 | def calc_variance(x_values, y_values, b0, b1): 92 | "Calculate the mean square deviation of the linear regeression line" 93 | # take the variance from the regression line instead of the data average 94 | sum_aux = sum([(y - b0 - b1 * x) ** 2 for x, y in zip(x_values, y_values)]) 95 | n = float(len(x_values)) 96 | return (1 / (n - 2.0)) * sum_aux 97 | 98 | 99 | def calc_prediction_interval(x_values, y_values, x_k, y_k, alpha): 100 | """Calculate the linear regression parameters for a set of n values 101 | then calculate the upper and lower prediction interval 102 | 103 | """ 104 | 105 | # calculate aux variables 106 | x_avg = mean(x_values) 107 | y_avg = mean(y_values) 108 | n = len(x_values) 109 | sum_xy = sum([(x_values[i] * y_values[i]) for i in range(n)]) 110 | sum_x2 = sum([(x_values[i] ** 2) for i in range(n)]) 111 | 112 | # calculate regression coefficients 113 | b1 = (sum_xy - (n * x_avg * y_avg)) / (sum_x2 - n * (x_avg ** 2)) 114 | b0 = y_avg - b1 * x_avg 115 | 116 | # calculate the t-value for the given alpha p-value 117 | t = calc_double_sided_student_t_value(1 - alpha, n - 2) 118 | 119 | # calculate the standard deviation 120 | sigma = math.sqrt(calc_variance(x_values, y_values, b0, b1)) 121 | 122 | # calculate the range 123 | sum_xi_xavg = sum([(x - x_avg) ** 2 for x in x_values], 0.0) 124 | aux = 1 + (1 / float(n)) + ((x_k - x_avg) ** 2) / sum_xi_xavg 125 | p_range = t * sigma * math.sqrt(aux) 126 | 127 | # combine the range with the x_k projection: 128 | return b0, b1, p_range, y_k + p_range, y_k - p_range, t 129 | -------------------------------------------------------------------------------- /psp2py/static/css/calendar.css: -------------------------------------------------------------------------------- 1 | .calendar{z-index:99;position:relative;display:none;border-top:2px solid #fff;border-right:2px solid #000;border-bottom:2px solid #000;border-left:2px solid #fff;font-size:11px;color:#000;cursor:default;background:#d4d0c8;font-family:tahoma,verdana,sans-serif;}.calendar table{border-top:1px solid #000;border-right:1px solid #fff;border-bottom:1px solid #fff;border-left:1px solid #000;font-size:11px;color:#000;cursor:default;background:#d4d0c8;font-family:tahoma,verdana,sans-serif;}.calendar .button{text-align:center;padding:1px;border-top:1px solid #fff;border-right:1px solid #000;border-bottom:1px solid #000;border-left:1px solid #fff;}.calendar .nav{background:transparent}.calendar thead .title{font-weight:bold;padding:1px;border:1px solid #000;background:#848078;color:#fff;text-align:center;}.calendar thead .name{border-bottom:1px solid #000;padding:2px;text-align:center;background:#f4f0e8;}.calendar thead .weekend{color:#f00;}.calendar thead .hilite{border-top:2px solid #fff;border-right:2px solid #000;border-bottom:2px solid #000;border-left:2px solid #fff;padding:0;background-color:#e4e0d8;}.calendar thead .active{padding:2px 0 0 2px;border-top:1px solid #000;border-right:1px solid #fff;border-bottom:1px solid #fff;border-left:1px solid #000;background-color:#c4c0b8;}.calendar tbody .day{width:2em;text-align:right;padding:2px 4px 2px 2px;}.calendar tbody .day.othermonth{font-size:80%;color:#aaa;}.calendar tbody .day.othermonth.oweekend{color:#faa;}.calendar table .wn{padding:2px 3px 2px 2px;border-right:1px solid #000;background:#f4f0e8;}.calendar tbody .rowhilite td{background:#e4e0d8;}.calendar tbody .rowhilite td.wn{background:#d4d0c8;}.calendar tbody td.hilite{padding:1px 3px 1px 1px;border-top:1px solid #fff;border-right:1px solid #000;border-bottom:1px solid #000;border-left:1px solid #fff;}.calendar tbody td.active{padding:2px 2px 0 2px;border-top:1px solid #000;border-right:1px solid #fff;border-bottom:1px solid #fff;border-left:1px solid #000;}.calendar tbody td.selected{font-weight:bold;border-top:1px solid #000;border-right:1px solid #fff;border-bottom:1px solid #fff;border-left:1px solid #000;padding:2px 2px 0 2px;background:#e4e0d8;}.calendar tbody td.weekend{color:#f00;}.calendar tbody td.today{font-weight:bold;color:#00f;}.calendar tbody .disabled{color:#999;}.calendar tbody .emptycell{visibility:hidden;}.calendar tbody .emptyrow{display:none;}.calendar tfoot .ttip{background:#f4f0e8;padding:1px;border:1px solid #000;background:#848078;color:#fff;text-align:center;}.calendar tfoot .hilite{border-top:1px solid #fff;border-right:1px solid #000;border-bottom:1px solid #000;border-left:1px solid #fff;padding:1px;background:#e4e0d8;}.calendar tfoot .active{padding:2px 0 0 2px;border-top:1px solid #000;border-right:1px solid #fff;border-bottom:1px solid #fff;border-left:1px solid #000;}.calendar .combo{position:absolute;display:none;width:4em;top:0;left:0;cursor:default;border-top:1px solid #fff;border-right:1px solid #000;border-bottom:1px solid #000;border-left:1px solid #fff;background:#e4e0d8;font-size:90%;padding:1px;z-index:100;}.calendar .combo .label,.calendar .combo .label-IEfix{text-align:center;padding:1px;}.calendar .combo .label-IEfix{width:4em;}.calendar .combo .active{background:#c4c0b8;padding:0;border-top:1px solid #000;border-right:1px solid #fff;border-bottom:1px solid #fff;border-left:1px solid #000;}.calendar .combo .hilite{background:#048;color:#fea;}.calendar td.time{border-top:1px solid #000;padding:1px 0;text-align:center;background-color:#f4f0e8;}.calendar td.time .hour,.calendar td.time .minute,.calendar td.time .ampm{padding:0 3px 0 4px;border:1px solid #889;font-weight:bold;background-color:#fff;}.calendar td.time .ampm{text-align:center;}.calendar td.time .colon{padding:0 2px 0 3px;font-weight:bold;}.calendar td.time span.hilite{border-color:#000;background-color:#766;color:#fff;}.calendar td.time span.active{border-color:#f00;background-color:#000;color:#0f0;}#CP_hourcont{z-index:99;padding:0;position:absolute;border:1px dashed #666;background-color:#eee;display:none;}#CP_minutecont{z-index:99;background-color:#ddd;padding:1px;position:absolute;width:45px;display:none;}.floatleft{float:left;}.CP_hour{z-index:99;padding:1px;font-family:Arial,Helvetica,sans-serif;font-size:9px;white-space:nowrap;cursor:pointer;width:35px;}.CP_minute{z-index:99;padding:1px;font-family:Arial,Helvetica,sans-serif;font-size:9px;white-space:nowrap;cursor:pointer;width:auto;}.CP_over{background-color:#fff;z-index:99} -------------------------------------------------------------------------------- /psp2py/static/css/handheld.css: -------------------------------------------------------------------------------- 1 | 2 | * { 3 | float: none; /* Screens are not big enough to account for floats */ 4 | font-size: 80%; /* Slightly reducing font size to reduce need to scroll */ 5 | background: #fff; /* As much contrast as possible */ 6 | color: #000; 7 | } -------------------------------------------------------------------------------- /psp2py/static/css/superfish-navbar.css: -------------------------------------------------------------------------------- 1 | 2 | /*** adding the class sf-navbar in addition to sf-menu creates an all-horizontal nav-bar menu ***/ 3 | .sf-navbar { 4 | background: #BDD2FF; 5 | height: 2.5em; 6 | padding-bottom: 2.5em; 7 | position: relative; 8 | } 9 | .sf-navbar li { 10 | background: #AABDE6; 11 | position: static; 12 | } 13 | .sf-navbar a { 14 | border-top: none; 15 | } 16 | .sf-navbar li ul { 17 | width: 44em; /*IE6 soils itself without this*/ 18 | } 19 | .sf-navbar li li { 20 | background: #BDD2FF; 21 | position: relative; 22 | } 23 | .sf-navbar li li ul { 24 | width: 13em; 25 | } 26 | .sf-navbar li li li { 27 | width: 100%; 28 | } 29 | .sf-navbar ul li { 30 | width: auto; 31 | float: left; 32 | } 33 | .sf-navbar a, .sf-navbar a:visited { 34 | border: none; 35 | } 36 | .sf-navbar li.current { 37 | background: #BDD2FF; 38 | } 39 | .sf-navbar li:hover, 40 | .sf-navbar li.sfHover, 41 | .sf-navbar li li.current, 42 | .sf-navbar a:focus, .sf-navbar a:hover, .sf-navbar a:active { 43 | background: #BDD2FF; 44 | } 45 | .sf-navbar ul li:hover, 46 | .sf-navbar ul li.sfHover, 47 | ul.sf-navbar ul li:hover li, 48 | ul.sf-navbar ul li.sfHover li, 49 | .sf-navbar ul a:focus, .sf-navbar ul a:hover, .sf-navbar ul a:active { 50 | background: #D1DFFF; 51 | } 52 | ul.sf-navbar li li li:hover, 53 | ul.sf-navbar li li li.sfHover, 54 | .sf-navbar li li.current li.current, 55 | .sf-navbar ul li li a:focus, .sf-navbar ul li li a:hover, .sf-navbar ul li li a:active { 56 | background: #E6EEFF; 57 | } 58 | ul.sf-navbar .current ul, 59 | ul.sf-navbar ul li:hover ul, 60 | ul.sf-navbar ul li.sfHover ul { 61 | left: 0; 62 | top: 2.5em; /* match top ul list item height */ 63 | } 64 | ul.sf-navbar .current ul ul { 65 | top: -999em; 66 | } 67 | 68 | .sf-navbar li li.current > a { 69 | font-weight: bold; 70 | } 71 | 72 | /*** point all arrows down ***/ 73 | /* point right for anchors in subs */ 74 | .sf-navbar ul .sf-sub-indicator { background-position: -10px -100px; } 75 | .sf-navbar ul a > .sf-sub-indicator { background-position: 0 -100px; } 76 | /* apply hovers to modern browsers */ 77 | .sf-navbar ul a:focus > .sf-sub-indicator, 78 | .sf-navbar ul a:hover > .sf-sub-indicator, 79 | .sf-navbar ul a:active > .sf-sub-indicator, 80 | .sf-navbar ul li:hover > a > .sf-sub-indicator, 81 | .sf-navbar ul li.sfHover > a > .sf-sub-indicator { 82 | background-position: -10px -100px; /* arrow hovers for modern browsers*/ 83 | } 84 | 85 | /*** remove shadow on first submenu ***/ 86 | .sf-navbar > li > ul { 87 | background: transparent; 88 | padding: 0; 89 | -moz-border-radius-bottomleft: 0; 90 | -moz-border-radius-topright: 0; 91 | -webkit-border-top-right-radius: 0; 92 | -webkit-border-bottom-left-radius: 0; 93 | } -------------------------------------------------------------------------------- /psp2py/static/css/superfish-vertical.css: -------------------------------------------------------------------------------- 1 | /*** adding sf-vertical in addition to sf-menu creates a vertical menu ***/ 2 | .sf-vertical, .sf-vertical li { 3 | width: 10em; 4 | } 5 | /* this lacks ul at the start of the selector, so the styles from the main CSS file override it where needed */ 6 | .sf-vertical li:hover ul, 7 | .sf-vertical li.sfHover ul { 8 | left: 10em; /* match ul width */ 9 | top: 0; 10 | } 11 | 12 | /*** alter arrow directions ***/ 13 | .sf-vertical .sf-sub-indicator { background-position: -10px 0; } /* IE6 gets solid image only */ 14 | .sf-vertical a > .sf-sub-indicator { background-position: 0 0; } /* use translucent arrow for modern browsers*/ 15 | 16 | /* hover arrow direction for modern browsers*/ 17 | .sf-vertical a:focus > .sf-sub-indicator, 18 | .sf-vertical a:hover > .sf-sub-indicator, 19 | .sf-vertical a:active > .sf-sub-indicator, 20 | .sf-vertical li:hover > a > .sf-sub-indicator, 21 | .sf-vertical li.sfHover > a > .sf-sub-indicator { 22 | background-position: -10px 0; /* arrow hovers for modern browsers*/ 23 | } -------------------------------------------------------------------------------- /psp2py/static/css/superfish.css: -------------------------------------------------------------------------------- 1 | 2 | /*** ESSENTIAL STYLES ***/ 3 | .sf-menu, .sf-menu * { 4 | margin: 0; 5 | padding: 0; 6 | list-style: none; 7 | } 8 | .sf-menu { 9 | line-height: 1.0; 10 | } 11 | .sf-menu ul { 12 | position: absolute; 13 | top: -999em; 14 | width: 10em; /* left offset of submenus need to match (see below) */ 15 | } 16 | .sf-menu ul li { 17 | width: 100%; 18 | } 19 | .sf-menu li:hover { 20 | visibility: inherit; /* fixes IE7 'sticky bug' */ 21 | } 22 | .sf-menu li { 23 | float: left; 24 | position: relative; 25 | } 26 | .sf-menu a { 27 | display: block; 28 | position: relative; 29 | } 30 | .sf-menu li:hover ul, 31 | .sf-menu li.sfHover ul { 32 | left: 0; 33 | top: 2.5em; /* match top ul list item height */ 34 | z-index: 99; 35 | } 36 | ul.sf-menu li:hover li ul, 37 | ul.sf-menu li.sfHover li ul { 38 | top: -999em; 39 | } 40 | ul.sf-menu li li:hover ul, 41 | ul.sf-menu li li.sfHover ul { 42 | left: 10em; /* match ul width */ 43 | top: 0; 44 | } 45 | ul.sf-menu li li:hover li ul, 46 | ul.sf-menu li li.sfHover li ul { 47 | top: -999em; 48 | } 49 | ul.sf-menu li li li:hover ul, 50 | ul.sf-menu li li li.sfHover ul { 51 | left: 10em; /* match ul width */ 52 | top: 0; 53 | } 54 | 55 | /*** DEMO SKIN ***/ 56 | .sf-menu { 57 | float: left; 58 | /*margin-bottom: 1em;*/ 59 | } 60 | .sf-menu a { 61 | border-left: 1px solid #fff; 62 | /*border-top: 1px solid #CFDEFF;*/ 63 | padding: .75em 1em; 64 | text-decoration:none; 65 | } 66 | .sf-menu a, .sf-menu a:visited { /* visited pseudo selector so IE6 applies text colour*/ 67 | color: #275b90;/*#13a;*/ 68 | } 69 | .sf-menu li { 70 | background: #dadada;/*#BDD2FF;*/ 71 | } 72 | .sf-menu li li { 73 | background: #AABDE6; 74 | } 75 | .sf-menu li li a { 76 | /*color: #13a;*/ 77 | } 78 | .sf-menu li li li { 79 | background: #9AAEDB; 80 | } 81 | .sf-menu li:hover, .sf-menu li.sfHover, 82 | .sf-menu a:focus, .sf-menu a:hover, .sf-menu a:active { 83 | background: #CFDEFF; 84 | outline: 0; 85 | } 86 | 87 | /*** arrows **/ 88 | .sf-menu a.sf-with-ul { 89 | padding-right: 2.25em; 90 | min-width: 1px; /* trigger IE7 hasLayout so spans position accurately */ 91 | } 92 | .sf-sub-indicator { 93 | position: absolute; 94 | display: block; 95 | right: .75em; 96 | top: 1.05em; /* IE6 only */ 97 | width: 10px; 98 | height: 10px; 99 | text-indent: -999em; 100 | overflow: hidden; 101 | background: url('../images/arrows-ffffff.png') no-repeat -10px -100px; /* 8-bit indexed alpha png. IE6 gets solid image only */ 102 | } 103 | a > .sf-sub-indicator { /* give all except IE6 the correct values */ 104 | top: .8em; 105 | background-position: 0 -100px; /* use translucent arrow for modern browsers*/ 106 | } 107 | /* apply hovers to modern browsers */ 108 | a:focus > .sf-sub-indicator, 109 | a:hover > .sf-sub-indicator, 110 | a:active > .sf-sub-indicator, 111 | li:hover > a > .sf-sub-indicator, 112 | li.sfHover > a > .sf-sub-indicator { 113 | background-position: -10px -100px; /* arrow hovers for modern browsers*/ 114 | } 115 | 116 | /* point right for anchors in subs */ 117 | .sf-menu ul .sf-sub-indicator { background-position: -10px 0; } 118 | .sf-menu ul a > .sf-sub-indicator { background-position: 0 0; } 119 | /* apply hovers to modern browsers */ 120 | .sf-menu ul a:focus > .sf-sub-indicator, 121 | .sf-menu ul a:hover > .sf-sub-indicator, 122 | .sf-menu ul a:active > .sf-sub-indicator, 123 | .sf-menu ul li:hover > a > .sf-sub-indicator, 124 | .sf-menu ul li.sfHover > a > .sf-sub-indicator { 125 | background-position: -10px 0; /* arrow hovers for modern browsers*/ 126 | } 127 | 128 | /*** shadows for all but IE6 ***/ 129 | .sf-shadow ul { 130 | background: url('../images/shadow.png') no-repeat bottom right; 131 | padding: 0 8px 9px 0; 132 | -moz-border-radius-bottomleft: 17px; 133 | -moz-border-radius-topright: 17px; 134 | -webkit-border-top-right-radius: 17px; 135 | -webkit-border-bottom-left-radius: 17px; 136 | } 137 | .sf-shadow ul.sf-shadow-off { 138 | background: transparent; 139 | } 140 | -------------------------------------------------------------------------------- /psp2py/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/psp2py/static/favicon.ico -------------------------------------------------------------------------------- /psp2py/static/images/arrows-ffffff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/psp2py/static/images/arrows-ffffff.png -------------------------------------------------------------------------------- /psp2py/static/images/css3buttons_backgrounds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/psp2py/static/images/css3buttons_backgrounds.png -------------------------------------------------------------------------------- /psp2py/static/images/css3buttons_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/psp2py/static/images/css3buttons_icons.png -------------------------------------------------------------------------------- /psp2py/static/images/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/psp2py/static/images/error.png -------------------------------------------------------------------------------- /psp2py/static/images/ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/psp2py/static/images/ok.png -------------------------------------------------------------------------------- /psp2py/static/images/poweredby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/psp2py/static/images/poweredby.png -------------------------------------------------------------------------------- /psp2py/static/images/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/psp2py/static/images/shadow.png -------------------------------------------------------------------------------- /psp2py/static/images/warn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/psp2py/static/images/warn.png -------------------------------------------------------------------------------- /psp2py/static/images/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/psp2py/static/images/warning.png -------------------------------------------------------------------------------- /psp2py/static/js/superfish.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Superfish v1.4.8 - jQuery menu widget 4 | * Copyright (c) 2008 Joel Birch 5 | * 6 | * Dual licensed under the MIT and GPL licenses: 7 | * http://www.opensource.org/licenses/mit-license.php 8 | * http://www.gnu.org/licenses/gpl.html 9 | * 10 | * CHANGELOG: http://users.tpg.com.au/j_birch/plugins/superfish/changelog.txt 11 | */ 12 | 13 | ;(function($){ 14 | $.fn.superfish = function(op){ 15 | 16 | var sf = $.fn.superfish, 17 | c = sf.c, 18 | $arrow = $([' »'].join('')), 19 | over = function(){ 20 | var $$ = $(this), menu = getMenu($$); 21 | clearTimeout(menu.sfTimer); 22 | $$.showSuperfishUl().siblings().hideSuperfishUl(); 23 | }, 24 | out = function(){ 25 | var $$ = $(this), menu = getMenu($$), o = sf.op; 26 | clearTimeout(menu.sfTimer); 27 | menu.sfTimer=setTimeout(function(){ 28 | o.retainPath=($.inArray($$[0],o.$path)>-1); 29 | $$.hideSuperfishUl(); 30 | if (o.$path.length && $$.parents(['li.',o.hoverClass].join('')).length<1){over.call(o.$path);} 31 | },o.delay); 32 | }, 33 | getMenu = function($menu){ 34 | var menu = $menu.parents(['ul.',c.menuClass,':first'].join(''))[0]; 35 | sf.op = sf.o[menu.serial]; 36 | return menu; 37 | }, 38 | addArrow = function($a){ $a.addClass(c.anchorClass).append($arrow.clone()); }; 39 | 40 | return this.each(function() { 41 | var s = this.serial = sf.o.length; 42 | var o = $.extend({},sf.defaults,op); 43 | o.$path = $('li.'+o.pathClass,this).slice(0,o.pathLevels).each(function(){ 44 | $(this).addClass([o.hoverClass,c.bcClass].join(' ')) 45 | .filter('li:has(ul)').removeClass(o.pathClass); 46 | }); 47 | sf.o[s] = sf.op = o; 48 | 49 | $('li:has(ul)',this)[($.fn.hoverIntent && !o.disableHI) ? 'hoverIntent' : 'hover'](over,out).each(function() { 50 | if (o.autoArrows) addArrow( $('>a:first-child',this) ); 51 | }) 52 | .not('.'+c.bcClass) 53 | .hideSuperfishUl(); 54 | 55 | var $a = $('a',this); 56 | $a.each(function(i){ 57 | var $li = $a.eq(i).parents('li'); 58 | $a.eq(i).focus(function(){over.call($li);}).blur(function(){out.call($li);}); 59 | }); 60 | o.onInit.call(this); 61 | 62 | }).each(function() { 63 | var menuClasses = [c.menuClass]; 64 | if (sf.op.dropShadows && !($.browser.msie && $.browser.version < 7)) menuClasses.push(c.shadowClass); 65 | $(this).addClass(menuClasses.join(' ')); 66 | }); 67 | }; 68 | 69 | var sf = $.fn.superfish; 70 | sf.o = []; 71 | sf.op = {}; 72 | sf.IE7fix = function(){ 73 | var o = sf.op; 74 | if ($.browser.msie && $.browser.version > 6 && o.dropShadows && o.animation.opacity!=undefined) 75 | this.toggleClass(sf.c.shadowClass+'-off'); 76 | }; 77 | sf.c = { 78 | bcClass : 'sf-breadcrumb', 79 | menuClass : 'sf-js-enabled', 80 | anchorClass : 'sf-with-ul', 81 | arrowClass : 'sf-sub-indicator', 82 | shadowClass : 'sf-shadow' 83 | }; 84 | sf.defaults = { 85 | hoverClass : 'sfHover', 86 | pathClass : 'overideThisToUse', 87 | pathLevels : 1, 88 | delay : 800, 89 | animation : {opacity:'show'}, 90 | speed : 'normal', 91 | autoArrows : true, 92 | dropShadows : true, 93 | disableHI : false, // true disables hoverIntent detection 94 | onInit : function(){}, // callback functions 95 | onBeforeShow: function(){}, 96 | onShow : function(){}, 97 | onHide : function(){} 98 | }; 99 | $.fn.extend({ 100 | hideSuperfishUl : function(){ 101 | var o = sf.op, 102 | not = (o.retainPath===true) ? o.$path : ''; 103 | o.retainPath = false; 104 | var $ul = $(['li.',o.hoverClass].join(''),this).add(this).not(not).removeClass(o.hoverClass) 105 | .find('>ul').hide().css('visibility','hidden'); 106 | o.onHide.call($ul); 107 | return this; 108 | }, 109 | showSuperfishUl : function(){ 110 | var o = sf.op, 111 | sh = sf.c.shadowClass+'-off', 112 | $ul = this.addClass(o.hoverClass) 113 | .find('>ul:hidden').css('visibility','visible'); 114 | sf.IE7fix.call($ul); 115 | o.onBeforeShow.call($ul); 116 | $ul.animate(o.animation,o.speed,function(){ sf.IE7fix.call($ul); o.onShow.call($ul); }); 117 | return this; 118 | } 119 | }); 120 | 121 | })(jQuery); 122 | -------------------------------------------------------------------------------- /psp2py/static/js/web2py_ajax.js: -------------------------------------------------------------------------------- 1 | function popup(url) { 2 | newwindow=window.open(url,'name','height=400,width=600'); 3 | if (window.focus) newwindow.focus(); 4 | return false; 5 | } 6 | function collapse(id) { jQuery('#'+id).slideToggle(); } 7 | function fade(id,value) { if(value>0) jQuery('#'+id).hide().fadeIn('slow'); else jQuery('#'+id).show().fadeOut('slow'); } 8 | function ajax(u,s,t) { 9 | query = ''; 10 | if (typeof s == "string") { 11 | d = jQuery(s).serialize(); 12 | if(d){ query = d; } 13 | } else { 14 | pcs = []; 15 | for(i=0; i0){query = pcs.join("&");} 20 | } 21 | jQuery.ajax({type: "POST", url: u, data: query, success: function(msg) { if(t) { if(t==':eval') eval(msg); else jQuery("#" + t).html(msg); } } }); 22 | } 23 | 24 | String.prototype.reverse = function () { return this.split('').reverse().join('');}; 25 | function web2py_ajax_init() { 26 | jQuery('.hidden').hide(); 27 | jQuery('.error').hide().slideDown('slow'); 28 | jQuery('.flash').click(function() { jQuery(this).fadeOut('slow'); return false; }); 29 | // jQuery('input[type=submit]').click(function(){var t=jQuery(this);t.hide();t.after('')}); 30 | jQuery('input.integer').live('keyup', function(){this.value=this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g,'').reverse();}); 31 | jQuery('input.double,input.decimal').live('keyup', function(){this.value=this.value.reverse().replace(/[^0-9\-\.,]|[\-](?=.)|[\.,](?=[0-9]*[\.,])/g,'').reverse();}); 32 | var confirm_message = (typeof w2p_ajax_confirm_message != 'undefined') ? w2p_ajax_confirm_message : "Are you sure you want to delete this object?"; 33 | jQuery("input[type='checkbox'].delete").live('click', function(){ if(this.checked) if(!confirm(confirm_message)) this.checked=false; }); 34 | var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d"; 35 | try {jQuery("input.date").live('focus',function() {Calendar.setup({ 36 | inputField:this, ifFormat:date_format, showsTime:false 37 | }); }); } catch(e) {}; 38 | var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S"; 39 | try { jQuery("input.datetime").live('focus', function() {Calendar.setup({ 40 | inputField:this, ifFormat:datetime_format, showsTime: true,timeFormat: "24" 41 | }); }); } catch(e) {}; 42 | 43 | jQuery("input.time").live('focus', function() { var el = jQuery(this); 44 | if (!el.hasClass('hasTimeEntry')) try { el.timeEntry(); } catch(e) {}; 45 | }); 46 | }; 47 | 48 | jQuery(function() { 49 | var flash = jQuery('.flash'); 50 | flash.hide(); 51 | if(flash.html()) flash.slideDown(); 52 | web2py_ajax_init(); 53 | }); 54 | function web2py_trap_form(action,target) { 55 | jQuery('#'+target+' form').each(function(i){ 56 | var form=jQuery(this); 57 | if(!form.hasClass('no_trap')) 58 | form.submit(function(obj){ 59 | jQuery('.flash').hide().html(''); 60 | web2py_ajax_page('post',action,form.serialize(),target); 61 | return false; 62 | }); 63 | }); 64 | } 65 | function web2py_ajax_page(method,action,data,target) { 66 | jQuery.ajax({'type':method,'url':action,'data':data, 67 | 'beforeSend':function(xhr) { 68 | xhr.setRequestHeader('web2py-component-location',document.location); 69 | xhr.setRequestHeader('web2py-component-element',target);}, 70 | 'complete':function(xhr,text){ 71 | var html=xhr.responseText; 72 | var content=xhr.getResponseHeader('web2py-component-content'); 73 | var command=xhr.getResponseHeader('web2py-component-command'); 74 | var flash=xhr.getResponseHeader('web2py-component-flash'); 75 | var t = jQuery('#'+target); 76 | if(content=='prepend') t.prepend(html); 77 | else if(content=='append') t.append(html); 78 | else if(content!='hide') t.html(html); 79 | web2py_trap_form(action,target); 80 | web2py_ajax_init(); 81 | if(command) eval(command); 82 | if(flash) jQuery('.flash').html(flash).slideDown(); 83 | } 84 | }); 85 | } 86 | function web2py_component(action,target) { 87 | jQuery(function(){ web2py_ajax_page('get',action,null,target); }); 88 | } 89 | function web2py_comet(url,onmessage,onopen,onclose) { 90 | if ("WebSocket" in window) { 91 | var ws = new WebSocket(url); 92 | ws.onopen = onopen?onopen:(function(){}); 93 | ws.onmessage = onmessage; 94 | ws.onclose = onclose?onclose:(function(){}); 95 | return true; // supported 96 | } else return false; // not supported 97 | } 98 | -------------------------------------------------------------------------------- /psp2py/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /welcome/default/user 3 | -------------------------------------------------------------------------------- /psp2py/views/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/psp2py/views/__init__.py -------------------------------------------------------------------------------- /psp2py/views/default/index.html: -------------------------------------------------------------------------------- 1 | {{left_sidebar_enabled=right_sidebar_enabled=False}} 2 | {{extend 'layout.html'}} 3 | 4 |
5 |

{{=T("Welcome to PSP support tool")}}

6 | In this application you can: 7 |
    8 |
  • {{=A(T("List projects"), _href=URL('projects','index'))}} or {{=A(T("Create"), _href=URL('projects','create'))}} one
  • 9 |
  • {{=A(T("Estimate project size (PROBE)"), _href=URL('probe','index'))}} using the {{=A(T("reuse library"), _href=URL('probe','library'))}} {{=A(T("categorization"), _href=URL('probe','categorize'))}} ({{=A(T("log-normbal distribution"), _href=URL('probe','normal_distribution.png'))}} )
  • 10 |
  • {{=A(T("Estimate project time"), _href=URL('estimate','index'))}}, calculate the {{=A(T("correlation"), _href=URL('estimate','correlation'))}} and {{=A(T("significance"), _href=URL('estimate','significance'))}} of your historical data ({{=A(T("Linear Regression"), _href=URL('estimate','linear_regression'))}}
  • 11 |
  • {{=A(T("Generate Reports"), _href=URL('reports','index'))}} of performance and quality ({{=A(T("projects"), _href=URL('reports','projects'))}}, {{=A(T("defects"), _href=URL('reports','defects'))}} including their {{=A(T("pareto distribution"), _href=URL('reports','pareto_distribution.png'))}} and {{=A(T("average fix time"), _href=URL('reports','average_fix_time.png'))}} )
  • 12 |
13 | 14 |

15 |


16 | 17 |

18 | For more information see http://code.google.com/p/rad2py 19 |

20 | -------------------------------------------------------------------------------- /psp2py/views/default/user.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 |

{{=T( request.args(0).replace('_',' ').capitalize() )}}

3 |
4 | {{=form}} 5 | {{if request.args(0)=='login':}} 6 | {{if not 'register' in auth.settings.actions_disabled:}} 7 |
register 8 | {{pass}} 9 | {{if not 'request_reset_password' in auth.settings.actions_disabled:}} 10 |
lost password 11 | {{pass}} 12 | {{pass}} 13 |
14 | 15 | 20 | -------------------------------------------------------------------------------- /psp2py/views/estimate/correlation.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 |

Correlation Test

3 | 4 | 5 | 6 | 7 | 8 | {{for h, l in zip(hours, loc):}} 9 | 10 | 11 | 12 | 13 | {{pass}} 14 |
Time (hours)Size (SLOC)
{{="%0.2f" % h}}{{=l}}
15 | 16 |

Correlation:

17 |
    18 |
  • r2 = {{=r2}} (correlation coefficient)
  • 19 |
  • Correlation Degree: {{=correlation}}
  • 20 |
21 | -------------------------------------------------------------------------------- /psp2py/views/estimate/index.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 |

Resource Estimate

3 | {{=form}} 4 | -------------------------------------------------------------------------------- /psp2py/views/estimate/significance.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 |

Significance Test

3 | 4 | 5 | 6 | 7 | 8 | {{for h, l in zip(hours, loc):}} 9 | 10 | 11 | 12 | 13 | {{pass}} 14 |
Time (hours)Size (SLOC)
{{="%0.2f" % h}}{{=l}}
15 | 16 |

Significance:

17 |
    18 |
  • r2 = {{=r2}} (correlation coefficient)
  • 19 |
  • t = {{=t}} (student-t value)
  • 20 |
  • p = {{=p}} (student-t probability)
  • 21 |
  • s = {{=s}} (1-p)
  • 22 |
  • Significance: {{=significance}}
  • 23 |
24 | -------------------------------------------------------------------------------- /psp2py/views/estimate/time_in_phase.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 |

Time to date summary

3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {{for t in times:}} 15 | 16 | 17 | 18 | 19 | 20 | {{pass}} 21 | 22 |
PhaseTime To Date%
{{=t[0]}}{{=pretty_time(t[1])}}{{="%d %%" % t[2]}}
23 | -------------------------------------------------------------------------------- /psp2py/views/generic.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 | {{""" 3 | 4 | You should not modify this file. 5 | It is used as default when a view is not provided for your controllers 6 | 7 | """}} 8 |

{{=' '.join(x.capitalize() for x in request.function.split('_'))}}

9 | {{if len(response._vars)==1:}} 10 | {{=response._vars.values()[0]}} 11 | {{elif len(response._vars)>1:}} 12 | {{=BEAUTIFY(response._vars)}} 13 | {{pass}} 14 | {{if request.is_local:}} 15 | {{=response.toolbar()}} 16 | {{pass}} 17 | -------------------------------------------------------------------------------- /psp2py/views/generic.json: -------------------------------------------------------------------------------- 1 | {{from gluon.serializers import json}}{{=XML(json(response._vars))}} 2 | -------------------------------------------------------------------------------- /psp2py/views/generic.load: -------------------------------------------------------------------------------- 1 | {{''' 2 | # License: Public Domain 3 | # Author: Iceberg at 21cn dot com 4 | 5 | With this generic.load file, you can use same function to serve two purposes. 6 | 7 | = regular action 8 | - ajax callback (when called with .load) 9 | 10 | Example modified from http://www.web2py.com/AlterEgo/default/show/252: 11 | 12 | def index(): 13 | return dict( 14 | part1='hello world', 15 | part2=LOAD(url=URL(r=request,f='auxiliary.load'),ajax=True)) 16 | 17 | def auxiliary(): 18 | form=SQLFORM.factory(Field('name')) 19 | if form.accepts(request.vars): 20 | response.flash = 'ok' 21 | return dict(message="Hello %s" % form.vars.name) 22 | return dict(form=form) 23 | 24 | Notice: 25 | 26 | - no need to set response.headers['web2py-response-flash'] 27 | - no need to return a string 28 | even if the function is called via ajax. 29 | 30 | '''}}{{if len(response._vars)==1:}}{{=response._vars.values()[0]}}{{else:}}{{=BEAUTIFY(response._vars)}}{{pass}} -------------------------------------------------------------------------------- /psp2py/views/generic.pdf: -------------------------------------------------------------------------------- 1 | {{ 2 | import os 3 | from gluon.contrib.generics import pdf_from_html 4 | filename = '%s/%s.html' % (request.controller,request.function) 5 | if os.path.exists(os.path.join(request.folder,'views',filename)): 6 | html=response.render(filename) 7 | else: 8 | html=BODY(BEAUTIFY(response._vars)).xml() 9 | pass 10 | =pdf_from_html(html) 11 | }} 12 | -------------------------------------------------------------------------------- /psp2py/views/generic.rss: -------------------------------------------------------------------------------- 1 | {{ 2 | ### 3 | # response._vars contains the dictionary returned by the controller action 4 | # for this to work the action must return something like 5 | # 6 | # dict(title=...,link=...,description=...,created_on='...',items=...) 7 | # 8 | # items is a list of dictionaries each with title, link, description, pub_date. 9 | ### 10 | from gluon.serializers import rss}}{{=XML(rss(response._vars))}} 11 | -------------------------------------------------------------------------------- /psp2py/views/generic.xml: -------------------------------------------------------------------------------- 1 | {{from gluon.serializers import xml}}{{=XML(xml(response._vars))}} 2 | -------------------------------------------------------------------------------- /psp2py/views/probe/categorize.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 |

Object size categorization

3 | 4 |

Normal (logarithmic) parameters

5 |
    6 |
  • Std_Dev: {{=std_dev}}
  • 7 |
  • Average: {{=avg_ln}}
  • 8 |
9 | 10 |

Objects and size ranges

11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {{for name, loc in locs.items():}} 20 | 21 | 22 | 23 | 24 | 25 | 26 | {{pass}} 27 |
Object NameSize RangeObject LOCObject ln(LOC)
{{=name}}{{=categorization.get(name)}}{{=objs[name]['loc']}}{{=loc}}
28 | 29 |

Distribution

30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | {{for size in PSP_SIZES:}} 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | {{pass}} 48 |
Size RangeRange Midpoint LOCRange Midpoint ln(LOC)QuantityPercentage
{{=size}}{{=midpoints.get(size, "")}}{{=midpoints_ln.get(size, "")}}{{=midpoints_q.get(size)}}{{=categorization and ("%0.2f" % (midpoints_q.get(size, 0)/float(len(categorization))*100.)) or ''}} %
49 | -------------------------------------------------------------------------------- /psp2py/views/probe/index.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 |

PROxy Based Estimation

3 | 4 |

{{=T("Identify objects")}}

5 | {{=form}} 6 | 7 |

8 | 9 |

10 |

{{=T("Objects")}}

11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {{for obj in sorted(objects.values(), key=lambda x: x['name']):}} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{pass}} 31 | 32 |
NameCategorybased onRelative sizePlanned LOC
{{=obj['name']}}{{=obj['category']}}{{=obj['function_name']}}{{=obj['relative_size']}}{{=obj['loc']}}
33 |
34 | -------------------------------------------------------------------------------- /psp2py/views/probe/library.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 |

PROBE reuse library

3 |

Query form

4 | {{=form}} 5 |

Objects

6 | {{=table}} 7 | -------------------------------------------------------------------------------- /psp2py/views/projects/create.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 |

Create PSP Project

3 | {{=form}} 4 | -------------------------------------------------------------------------------- /psp2py/views/projects/edit.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 |

Edit Project

3 | {{=form}} 4 | -------------------------------------------------------------------------------- /psp2py/views/projects/index.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 |

PSP Projects List

3 | {{=form}} 4 | -------------------------------------------------------------------------------- /psp2py/views/projects/show.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 |

Project: {{=project.name}}

3 | {{=form}} 4 | {{=FORM( 5 | INPUT(_type="submit", _value=T("Edit")), 6 | _action=URL('projects', 'edit', args=('psp_project', project.project_id)), )}} 7 |

Time Summary

8 | {{=SQLTABLE(times, headers="fieldname:capitalize")}} 9 |

Defects

10 | {{=SQLTABLE(defects, headers="fieldname:capitalize")}} 11 | -------------------------------------------------------------------------------- /psp2py/views/reports/defects.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 |

Defect Type Standard Report

3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {{for defect_type, defect_name in sorted(PSP_DEFECT_TYPES.items()):}} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | {{pass}} 28 |
No.NameDescriptionQuantityFreq. %Fix TimeTime %Average
{{=defect_type}}{{=defect_name}}{{=PSP_DEFECT_DESC[defect_type]}}{{='%d' % (defect_count_per_type.get(defect_type, 0))}}{{=('%.2f' % (defect_count_per_type.get(defect_type, 0)/float(defect_count)*100.0)) if defect_count else ""}} %{{=pretty_time(defect_fix_time_per_type.get(defect_type, 0))}} {{=('%.2f' % (defect_fix_time_per_type.get(defect_type, 0) / float(total_fix_time) * 100.)) if total_fix_time else ""}} % {{=defect_count_per_type.get(defect_type, 0) and pretty_time(defect_fix_time_per_type.get(defect_type, 0) / float(defect_count_per_type.get(defect_type, 0))) or ''}}
29 | -------------------------------------------------------------------------------- /psp2py/views/reports/index.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 |

General Report

3 | 4 | 5 | 6 | 17 | 29 | 38 | 39 |
7 |

Productivity

8 |
    9 |
  • LOC per hour: {{="%0.2f" % loc_per_hour}}
  • 10 |
  • Total LOC: {{=total_loc}}
  • 11 |
  • Total Time: {{="%0.2f" % (total_time / 60. /60.) }} hours
  • 12 |
  • Interruption Time: {{="%0.2f" % (interruption_time / 60. /60.) }} hours
  • 13 |
  • Planned Time: {{="%0.2f" % (planned_time / 60. /60.) }} hours
  • 14 |
  • CPI: {{="%0.2f" % (cost_performance_index) }} (plan/actual time)
  • 15 |
16 |
18 |

Defect Removal Efficiency

19 |
    20 |
  • Process Yield: {{=process_yield}} % (removal before compile)
  • 21 |
  • Defects per hour: {{="%0.2f" % total_defects_injected_per_hour}} (injected/removed)
  • 22 |
  • Test Defects: {{="%0.2f" % test_defects_per_kloc}} removed per KLOC
  • 23 |
  • Total Defects: {{="%0.2f" % total_defects_per_kloc}} removed per KLOC
  • 24 |
  • Defect count: {{=total_defect_count}}
  • 25 |
  • Fix Time: {{=pretty_time(total_fix_time)}}
  • 26 |
  • Average Fix Time: {{=pretty_time(average_fix_time)}}
  • 27 |
28 |
30 |

Cost of Quality

31 |
    32 |
  • Appraisal: {{="%0.2f" % appraisal}} % (total review time)
  • 33 |
  • Failure: {{="%0.2f" % failure}} % (total compile + test time)
  • 34 |
  • COQ: {{="%0.2f" % (appraisal + failure)}} % (appraisal + failure)
  • 35 |
  • COQ A/FR: {{="%0.2f" % (appraisal_failure_ratio)}} (appraisal / failure)
  • 36 |
37 |
40 | 41 |

Report per phase

42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | {{for phase in PSP_PHASES:}} 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | {{pass}} 70 |
PhasePlanned Time
To Date
Planned Time
To Date %
Actual Time
To Date
Actual Time
To Date %
Defects
injected
Defects
removed
YieldDefects
per hour
Defect Removal
Leverage (DRL)
{{=phase}}{{=pretty_time(planned_time_per_phase.get(phase, 0))}} {{='%.2f' % (planned_time_per_phase.get(phase, 0) / planned_time * 100.)}} %{{=pretty_time(times_per_phase.get(phase))}}{{='%.2f' % (times_per_phase.get(phase, 0) / total_time * 100.)}} %{{='%d' % (defects_injected_per_phase.get(phase) or 0)}}{{='%d' % (defects_removed_per_phase.get(phase) or 0)}}{{=yields_per_phase.get(phase) or ''}} %{{='%.2f' % (defects_per_hour_per_phase.get(phase) or 0)}}{{='%.2f' % (defect_removal_leverage.get(phase) or 0)}}
71 | -------------------------------------------------------------------------------- /psp2py/views/reports/projects.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 |

Projects Summary Report

3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | {{for project in projects:}} 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | {{pass}} 41 |
ProjectDescriptionPlan.
Time
LPI
Time
UPI
Time
Actual
Time
Int.
Time
CPIPlan.
LOC
Actual
LOC
DefectsFix Time
{{=project.psp_project.name}}{{=project.psp_project.description}}{{=pretty_time(project.sum_plan)}}{{="%.2f" % (project.psp_project.time_lpi or 0)}} h{{="%.2f" % (project.psp_project.time_upi or 0)}} h{{=pretty_time(project.sum_actual)}}{{=pretty_time(project.sum_interruption)}}{{= ('%.2f' % (project.sum_plan / float(project.sum_actual) if (project.sum_plan and project.sum_actual) else 0 )) or '' }}{{=project.psp_project.planned_loc or ''}}{{=project.psp_project.actual_loc or ''}}{{=defects_per_project.get(project.psp_project.project_id, '')}}{{=pretty_time(fix_time_per_project.get(project.psp_project.project_id, 0))}}
42 | -------------------------------------------------------------------------------- /psp2py/views/web2py_ajax.html: -------------------------------------------------------------------------------- 1 | {{ 2 | response.files.insert(0,URL('static','js/jquery.js')) 3 | response.files.insert(1,URL('static','css/calendar.css')) 4 | response.files.insert(2,URL('static','js/calendar.js')) 5 | for _item in response.meta or []:}} 6 | {{ 7 | pass 8 | for _k,_file in enumerate(response.files or []): 9 | if _file in response.files[:_k]: 10 | continue 11 | _file0=_file.lower().split('?')[0] 12 | if _file0.endswith('.css'):}} 13 | {{ 14 | elif _file0.endswith('.js'):}} 15 | {{ 16 | pass 17 | pass 18 | }} 19 | 25 | 26 | -------------------------------------------------------------------------------- /psp2py/views/wiki/edit.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 |

Edit Wiki

3 | {{=form}} 4 | -------------------------------------------------------------------------------- /psp2py/views/wiki/index.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 |

Wiki Page Index

3 |
    4 | {{for row in rows:}} 5 |
  • {{=A(row.title, _href=URL("view", args=row.page.split("/")))}}
  • 6 | {{pass}} 7 |
8 | 9 | {{=FORM( 10 | INPUT(_type="submit", _value=T("New Page")), 11 | _action=URL('wiki', 'edit'))}} 12 | -------------------------------------------------------------------------------- /psp2py/views/wiki/load.html: -------------------------------------------------------------------------------- 1 |

{{=title}}

2 | {{=text}} 3 | -------------------------------------------------------------------------------- /psp2py/views/wiki/view.html: -------------------------------------------------------------------------------- 1 | {{extend 'layout.html'}} 2 |

{{=title}}

3 | 4 | {{=text}} 5 | 6 | {{=FORM( 7 | INPUT(_type="submit", _value=T("Edit")), 8 | _action=URL('wiki', 'edit', args=request.args))}} 9 | -------------------------------------------------------------------------------- /screenshots/ide2py-0.05-ubuntu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/ide2py-0.05-ubuntu.png -------------------------------------------------------------------------------- /screenshots/ide2py-v0.03-ubuntu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/ide2py-v0.03-ubuntu.png -------------------------------------------------------------------------------- /screenshots/proof-of-concept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/proof-of-concept.png -------------------------------------------------------------------------------- /screenshots/prototipe-alpha-win7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/prototipe-alpha-win7.png -------------------------------------------------------------------------------- /screenshots/psp_average_fix_times.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/psp_average_fix_times.png -------------------------------------------------------------------------------- /screenshots/psp_defect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/psp_defect.png -------------------------------------------------------------------------------- /screenshots/psp_defect_report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/psp_defect_report.png -------------------------------------------------------------------------------- /screenshots/psp_defects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/psp_defects.png -------------------------------------------------------------------------------- /screenshots/psp_estimate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/psp_estimate.png -------------------------------------------------------------------------------- /screenshots/psp_linear_regression.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/psp_linear_regression.png -------------------------------------------------------------------------------- /screenshots/psp_metadata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/psp_metadata.png -------------------------------------------------------------------------------- /screenshots/psp_normal_distribution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/psp_normal_distribution.png -------------------------------------------------------------------------------- /screenshots/psp_pareto_distribution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/psp_pareto_distribution.png -------------------------------------------------------------------------------- /screenshots/psp_plan_summary_times.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/psp_plan_summary_times.png -------------------------------------------------------------------------------- /screenshots/psp_probe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/psp_probe.png -------------------------------------------------------------------------------- /screenshots/psp_project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/psp_project.png -------------------------------------------------------------------------------- /screenshots/psp_report_projects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/psp_report_projects.png -------------------------------------------------------------------------------- /screenshots/psp_reports.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/psp_reports.png -------------------------------------------------------------------------------- /screenshots/psp_reuse_library.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/psp_reuse_library.png -------------------------------------------------------------------------------- /screenshots/psp_toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/psp_toolbar.png -------------------------------------------------------------------------------- /screenshots/psp_wiki.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/psp_wiki.png -------------------------------------------------------------------------------- /screenshots/pytong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/pytong.png -------------------------------------------------------------------------------- /screenshots/wxPyDiff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reingart/rad2py/e4802ade132d569225e8be7eca830d2f888aa5f9/screenshots/wxPyDiff.png -------------------------------------------------------------------------------- /setup_qdb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from distutils.core import setup 4 | try: 5 | import py2exe 6 | from nsis import build_installer 7 | except: 8 | build_installer = None 9 | 10 | import qdb 11 | 12 | setup(name='qdb', 13 | version=qdb.__version__, 14 | description='PDB-like Client/Server Debugger for Python', 15 | author='Mariano Reingart', 16 | author_email='reingart@gmail.com', 17 | url='http://code.google.com/p/rad2py/wiki/QdbRemotePythonDebugger', 18 | packages=['qdb'], 19 | console=['qdb/qdb.py'], 20 | cmdclass = {"py2exe": build_installer}, 21 | ) 22 | 23 | --------------------------------------------------------------------------------