diff --git a/Scripts/Activate.ps1 b/Scripts/Activate.ps1 index b3ceae7..dd2208c 100644 --- a/Scripts/Activate.ps1 +++ b/Scripts/Activate.ps1 @@ -247,226 +247,256 @@ Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH $Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" # SIG # Begin signature block -# MIIpjwYJKoZIhvcNAQcCoIIpgDCCKXwCAQExDzANBglghkgBZQMEAgEFADB5Bgor +# MIIvJAYJKoZIhvcNAQcCoIIvFTCCLxECAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBnL745ElCYk8vk -# dBtMuQhLeWJ3ZGfzKW4DHCYzAn+QB6CCDi8wggawMIIEmKADAgECAhAIrUCyYNKc -# TJ9ezam9k67ZMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK +# dBtMuQhLeWJ3ZGfzKW4DHCYzAn+QB6CCE8MwggWQMIIDeKADAgECAhAFmxtXno4h +# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV -# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0z -# NjA0MjgyMzU5NTlaMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg -# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg -# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw -# ggIKAoICAQDVtC9C0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0 -# JAfhS0/TeEP0F9ce2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJr -# Q5qZ8sU7H/Lvy0daE6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhF -# LqGfLOEYwhrMxe6TSXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+F -# LEikVoQ11vkunKoAFdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh -# 3K3kGKDYwSNHR7OhD26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJ -# wZPt4bRc4G/rJvmM1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQay -# g9Rc9hUZTO1i4F4z8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbI -# YViY9XwCFjyDKK05huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchAp -# QfDVxW0mdmgRQRNYmtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRro -# OBl8ZhzNeDhFMJlP/2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IB -# WTCCAVUwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+ -# YXsIiGX0TkIwHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0P -# AQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAk -# BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAC -# hjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9v -# dEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5j -# b20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAED -# MAgGBmeBDAEEATANBgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql -# +Eg08yy25nRm95RysQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFF -# UP2cvbaF4HZ+N3HLIvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1h -# mYFW9snjdufE5BtfQ/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3Ryw -# YFzzDaju4ImhvTnhOE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5Ubdld -# AhQfQDN8A+KVssIhdXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw -# 8MzK7/0pNVwfiThV9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnP -# LqR0kq3bPKSchh/jwVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatE -# QOON8BUozu3xGFYHKi8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bn -# KD+sEq6lLyJsQfmCXBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQji -# WQ1tygVQK+pKHJ6l/aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbq -# yK+p/pQd52MbOoZWeE4wggd3MIIFX6ADAgECAhAHHxQbizANJfMU6yMM0NHdMA0G -# CSqGSIb3DQEBCwUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg -# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcg -# UlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwHhcNMjIwMTE3MDAwMDAwWhcNMjUwMTE1 -# MjM1OTU5WjB8MQswCQYDVQQGEwJVUzEPMA0GA1UECBMGT3JlZ29uMRIwEAYDVQQH -# EwlCZWF2ZXJ0b24xIzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9u -# MSMwIQYDVQQDExpQeXRob24gU29mdHdhcmUgRm91bmRhdGlvbjCCAiIwDQYJKoZI -# hvcNAQEBBQADggIPADCCAgoCggIBAKgc0BTT+iKbtK6f2mr9pNMUTcAJxKdsuOiS -# YgDFfwhjQy89koM7uP+QV/gwx8MzEt3c9tLJvDccVWQ8H7mVsk/K+X+IufBLCgUi -# 0GGAZUegEAeRlSXxxhYScr818ma8EvGIZdiSOhqjYc4KnfgfIS4RLtZSrDFG2tN1 -# 6yS8skFa3IHyvWdbD9PvZ4iYNAS4pjYDRjT/9uzPZ4Pan+53xZIcDgjiTwOh8VGu -# ppxcia6a7xCyKoOAGjvCyQsj5223v1/Ig7Dp9mGI+nh1E3IwmyTIIuVHyK6Lqu35 -# 2diDY+iCMpk9ZanmSjmB+GMVs+H/gOiofjjtf6oz0ki3rb7sQ8fTnonIL9dyGTJ0 -# ZFYKeb6BLA66d2GALwxZhLe5WH4Np9HcyXHACkppsE6ynYjTOd7+jN1PRJahN1oE -# RzTzEiV6nCO1M3U1HbPTGyq52IMFSBM2/07WTJSbOeXjvYR7aUxK9/ZkJiacl2iZ -# I7IWe7JKhHohqKuceQNyOzxTakLcRkzynvIrk33R9YVqtB4L6wtFxhUjvDnQg16x -# ot2KVPdfyPAWd81wtZADmrUtsZ9qG79x1hBdyOl4vUtVPECuyhCxaw+faVjumapP -# Unwo8ygflJJ74J+BYxf6UuD7m8yzsfXWkdv52DjL74TxzuFTLHPyARWCSCAbzn3Z -# Ily+qIqDAgMBAAGjggIGMIICAjAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiI -# ZfROQjAdBgNVHQ4EFgQUt/1Teh2XDuUj2WW3siYWJgkZHA8wDgYDVR0PAQH/BAQD -# AgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0 -# dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWdu -# aW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRwOi8vY3JsNC5k -# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZT -# SEEzODQyMDIxQ0ExLmNybDA+BgNVHSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUF -# BwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwgZQGCCsGAQUFBwEBBIGH -# MIGEMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXAYIKwYB -# BQUHMAKGUGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0 -# ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3J0MAwGA1UdEwEB -# /wQCMAAwDQYJKoZIhvcNAQELBQADggIBABxv4AeV/5ltkELHSC63fXAFYS5tadcW -# TiNc2rskrNLrfH1Ns0vgSZFoQxYBFKI159E8oQQ1SKbTEubZ/B9kmHPhprHya08+ -# VVzxC88pOEvz68nA82oEM09584aILqYmj8Pj7h/kmZNzuEL7WiwFa/U1hX+XiWfL -# IJQsAHBla0i7QRF2de8/VSF0XXFa2kBQ6aiTsiLyKPNbaNtbcucaUdn6vVUS5izW -# OXM95BSkFSKdE45Oq3FForNJXjBvSCpwcP36WklaHL+aHu1upIhCTUkzTHMh8b86 -# WmjRUqbrnvdyR2ydI5l1OqcMBjkpPpIV6wcc+KY/RH2xvVuuoHjlUjwq2bHiNoX+ -# W1scCpnA8YTs2d50jDHUgwUo+ciwpffH0Riq132NFmrH3r67VaN3TuBxjI8SIZM5 -# 8WEDkbeoriDk3hxU8ZWV7b8AW6oyVBGfM06UgkfMb58h+tJPrFx8VI/WLq1dTqMf -# ZOm5cuclMnUHs2uqrRNtnV8UfidPBL4ZHkTcClQbCoz0UbLhkiDvIS00Dn+BBcxw -# /TKqVL4Oaz3bkMSsM46LciTeucHY9ExRVt3zy7i149sd+F4QozPqn7FrSVHXmem3 -# r7bjyHTxOgqxRCVa18Vtx7P/8bYSBeS+WHCKcliFCecspusCDSlnRUjZwyPdP0VH -# xaZg2unjHY3rMYIatjCCGrICAQEwfTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMO -# RGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29k -# ZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExAhAHHxQbizANJfMU6yMM -# 0NHdMA0GCWCGSAFlAwQCAQUAoIHEMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEE -# MBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCBn -# AZ6P7YvTwq0fbF62o7E75R0LxsW5OtyYiFESQckLhjBYBgorBgEEAYI3AgEMMUow -# SKBGgEQAQgB1AGkAbAB0ADoAIABSAGUAbABlAGEAcwBlAF8AdgAzAC4AMQAwAC4A -# NwBfADIAMAAyADIAMAA5ADAANQAuADAAMTANBgkqhkiG9w0BAQEFAASCAgB0gPSc -# 6sIspVwJh+Cymxnsu7ijHvRyTzmrVD7qWYvH8hx5jmXGOjt0RUHGTFUAYNo3CNul -# caAO7rAsP877JU/i+CckdqtH0iT6LF2uLlGfXNW7hHHr2ZxERI1pw24R67HKE/vf -# VnG33FOv8iLwrzZ1kwMvFZDZ0hMutZ1+W73yzREXKR3Eyo8FUSIVm7VqydpSQvNp -# uK+XEuUclk4UJ04nnQWkOlRHq/IMscoqUMMd9c+Rt6kAYwCT5Q0r8TlASxrzcRT9 -# mq3oin/HSfBMVrvQ+cP6Z4SqAVDFjdEOqmRGqbUycM/KXJBqyR/aipar083OpW5M -# aYgCsKcM6RzxdKLKZFXsgD+f9sCBO87K8j0mB7zJ8u1Ankr1ZsKM5cJhOrGqSuxd -# D6sBbFZmgMRw1zRrE7RvwFpbl2pqOTqnNM0aygWpLTw6BT6BmqwHKothsFoUAvU6 -# JCXUm51hhISSaiQH5QU2uzLRhek9KeLJBNUDMDtz2ZTuHOVrwHu6x/DOCPYceFQ+ -# WqbdAvapigTf8QEYVUcAnb5GyrpFadnWwXzravYbht4Ooy+qgkQo3pD5d13rXQH+ -# sNyIiidRC1bGH9LqESRANpzLVeIieR/0h+NT6Am9eShm331nvI3nK70yZFjo1auT -# 4NtbfZ9+qFNRERTWuZu14ietZ5cvuBn8LQQ22KGCF0Mwghc/BgorBgEEAYI3AwMB -# MYIXLzCCFysGCSqGSIb3DQEHAqCCFxwwghcYAgEDMQ8wDQYJYIZIAWUDBAIBBQAw -# dwYLKoZIhvcNAQkQAQSgaARmMGQCAQEGCWCGSAGG/WwHATAxMA0GCWCGSAFlAwQC -# AQUABCCB/JRH4mUeWdX+VQKL48ZyhRT0MP0P8a59gSuiyyhRpwIQCOalIO1Qocue -# kO+4D3drKBgPMjAyMjA5MDUxNDE1MjdaoIITDTCCBsYwggSuoAMCAQICEAp6Soie -# yZlCkAZjOE2Gl50wDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNV -# BAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0 -# IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yMjAzMjkwMDAwMDBa -# Fw0zMzAzMTQyMzU5NTlaMEwxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2Vy -# dCwgSW5jLjEkMCIGA1UEAxMbRGlnaUNlcnQgVGltZXN0YW1wIDIwMjIgLSAyMIIC -# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuSqWI6ZcvF/WSfAVghj0M+7M -# XGzj4CUu0jHkPECu+6vE43hdflw26vUljUOjges4Y/k8iGnePNIwUQ0xB7pGbumj -# S0joiUF/DbLW+YTxmD4LvwqEEnFsoWImAdPOw2z9rDt+3Cocqb0wxhbY2rzrsvGD -# 0Z/NCcW5QWpFQiNBWvhg02UsPn5evZan8Pyx9PQoz0J5HzvHkwdoaOVENFJfD1De -# 1FksRHTAMkcZW+KYLo/Qyj//xmfPPJOVToTpdhiYmREUxSsMoDPbTSSF6IKU4S8D -# 7n+FAsmG4dUYFLcERfPgOL2ivXpxmOwV5/0u7NKbAIqsHY07gGj+0FmYJs7g7a5/ -# KC7CnuALS8gI0TK7g/ojPNn/0oy790Mj3+fDWgVifnAs5SuyPWPqyK6BIGtDich+ -# X7Aa3Rm9n3RBCq+5jgnTdKEvsFR2wZBPlOyGYf/bES+SAzDOMLeLD11Es0MdI1DN -# kdcvnfv8zbHBp8QOxO9APhk6AtQxqWmgSfl14ZvoaORqDI/r5LEhe4ZnWH5/H+gr -# 5BSyFtaBocraMJBr7m91wLA2JrIIO/+9vn9sExjfxm2keUmti39hhwVo99Rw40KV -# 6J67m0uy4rZBPeevpxooya1hsKBBGBlO7UebYZXtPgthWuo+epiSUc0/yUTngIsp -# QnL3ebLdhOon7v59emsCAwEAAaOCAYswggGHMA4GA1UdDwEB/wQEAwIHgDAMBgNV -# HRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMCAGA1UdIAQZMBcwCAYG -# Z4EMAQQCMAsGCWCGSAGG/WwHATAfBgNVHSMEGDAWgBS6FtltTYUvcyl2mi91jGog -# j57IbzAdBgNVHQ4EFgQUjWS3iSH+VlhEhGGn6m8cNo/drw0wWgYDVR0fBFMwUTBP -# oE2gS4ZJaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0 -# UlNBNDA5NlNIQTI1NlRpbWVTdGFtcGluZ0NBLmNybDCBkAYIKwYBBQUHAQEEgYMw -# gYAwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBYBggrBgEF -# BQcwAoZMaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3Rl -# ZEc0UlNBNDA5NlNIQTI1NlRpbWVTdGFtcGluZ0NBLmNydDANBgkqhkiG9w0BAQsF -# AAOCAgEADS0jdKbR9fjqS5k/AeT2DOSvFp3Zs4yXgimcQ28BLas4tXARv4QZiz9d -# 5YZPvpM63io5WjlO2IRZpbwbmKrobO/RSGkZOFvPiTkdcHDZTt8jImzV3/ZZy6HC -# 6kx2yqHcoSuWuJtVqRprfdH1AglPgtalc4jEmIDf7kmVt7PMxafuDuHvHjiKn+8R -# yTFKWLbfOHzL+lz35FO/bgp8ftfemNUpZYkPopzAZfQBImXH6l50pls1klB89Bem -# h2RPPkaJFmMga8vye9A140pwSKm25x1gvQQiFSVwBnKpRDtpRxHT7unHoD5PELkw -# NuTzqmkJqIt+ZKJllBH7bjLx9bs4rc3AkxHVMnhKSzcqTPNc3LaFwLtwMFV41pj+ -# VG1/calIGnjdRncuG3rAM4r4SiiMEqhzzy350yPynhngDZQooOvbGlGglYKOKGuk -# zp123qlzqkhqWUOuX+r4DwZCnd8GaJb+KqB0W2Nm3mssuHiqTXBt8CzxBxV+NbTm -# tQyimaXXFWs1DoXW4CzM4AwkuHxSCx6ZfO/IyMWMWGmvqz3hz8x9Fa4Uv4px38qX -# sdhH6hyF4EVOEhwUKVjMb9N/y77BDkpvIJyu2XMyWQjnLZKhGhH+MpimXSuX4IvT -# nMxttQ2uR2M4RxdbbxPaahBuH0m3RFu0CAqHWlkEdhGhp3cCExwwggauMIIElqAD -# AgECAhAHNje3JFR82Ees/ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYT -# AlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2Vy -# dC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMjAz -# MjMwMDAwMDBaFw0zNzAzMjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQK -# Ew5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBS -# U0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUA -# A4ICDwAwggIKAoICAQDGhjUGSbPBPXJJUVXHJQPE8pE3qZdRodbSg9GeTKJtoLDM -# g/la9hGhRBVCX6SI82j6ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9/UO0hNoR8XOx -# s+4rgISKIhjf69o9xBd/qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXnHwZljZQp09ns -# ad/ZkIdGAHvbREGJ3HxqV3rwN3mfXazL6IRktFLydkf3YYMZ3V+0VAshaG43IbtA -# rF+y3kp9zvU5EmfvDqVjbOSmxR3NNg1c1eYbqMFkdECnwHLFuk4fsbVYTXn+149z -# k6wsOeKlSNbwsDETqVcplicu9Yemj052FVUmcJgmf6AaRyBD40NjgHt1biclkJg6 -# OBGz9vae5jtb7IHeIhTZgirHkr+g3uM+onP65x9abJTyUpURK1h0QCirc0PO30qh -# HGs4xSnzyqqWc0Jon7ZGs506o9UD4L/wojzKQtwYSH8UNM/STKvvmz3+DrhkKvp1 -# KCRB7UK/BZxmSVJQ9FHzNklNiyDSLFc1eSuo80VgvCONWPfcYd6T/jnA+bIwpUzX -# 6ZhKWD7TA4j+s4/TXkt2ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk42PgpuE+9sJ0 -# sj8eCXbsq11GdeJgo1gJASgADoRU7s7pXcheMBK9Rp6103a50g5rmQzSM7TNsQID -# AQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuhbZbU2F -# L3MpdpovdYxqII+eyG8wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08w -# DgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEB -# BGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsG -# AQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz -# dGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdp -# Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgG -# BmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBAH1ZjsCTtm+Y -# qUQiAX5m1tghQuGwGC4QTRPPMFPOvxj7x1Bd4ksp+3CKDaopafxpwc8dB+k+YMjY -# C+VcW9dth/qEICU0MWfNthKWb8RQTGIdDAiCqBa9qVbPFXONASIlzpVpP0d3+3J0 -# FNf/q0+KLHqrhc1DX+1gtqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQcAp876i8dU+6 -# WvepELJd6f8oVInw1YpxdmXazPByoyP6wCeCRK6ZJxurJB4mwbfeKuv2nrF5mYGj -# VoarCkXJ38SNoOeY+/umnXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+jSbl3ZpHxcpzp -# SwJSpzd+k1OsOx0ISQ+UzTl63f8lY5knLD0/a6fxZsNBzU+2QJshIUDQtxMkzdwd -# eDrknq3lNHGS1yZr5Dhzq6YBT70/O3itTK37xJV77QpfMzmHQXh6OOmc4d0j/R0o -# 08f56PGYX/sr2H7yRp11LB4nLCbbbxV7HhmLNriT1ObyF5lZynDwN7+YAN8gFk8n -# +2BnFqFmut1VwDophrCYoCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR81fZvAT6gt4y -# 3wSJ8ADNXcL50CN/AAvkdgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2VVQrH4D6wPIO -# K+XW+6kvRBVK5xMOHds3OBqhK/bt1nz8MIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv -# 21DiCEAYWjANBgkqhkiG9w0BAQwFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMM -# RGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQD -# ExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcN -# MzExMTA5MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQg -# SW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2Vy -# dCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC -# AQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf -# 8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1 -# mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe -# 7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecx -# y9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX -# 2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX -# 9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp49 -# 3ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCq -# sWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFH -# dL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauG -# i0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYw -# DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08w -# HwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGG -# MHkGCCsGAQUFBwEBBG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNl -# cnQuY29tMEMGCCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20v -# RGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0 -# dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5j -# cmwwEQYDVR0gBAowCDAGBgRVHSAAMA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXn -# OF+go3QbPbYW1/e/Vwe9mqyhhyzshV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23 -# OO/0/4C5+KH38nLeJLxSA8hO0Cre+i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFI -# tJnLnU+nBgMTdydE1Od/6Fmo8L8vC6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7s -# pNU96LHc/RzY9HdaXFSMb++hUD38dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgi -# wbJZ9VVrzyerbHbObyMt9H5xaiNrIv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cB -# qZ9Xql4o4rmUMYIDdjCCA3ICAQEwdzBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMO -# RGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNB -# NDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBAhAKekqInsmZQpAGYzhNhpedMA0G -# CWCGSAFlAwQCAQUAoIHRMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkq -# hkiG9w0BCQUxDxcNMjIwOTA1MTQxNTI3WjArBgsqhkiG9w0BCRACDDEcMBowGDAW -# BBSFCPOGUVyz0wd9trS3wH8bSl5B3jAvBgkqhkiG9w0BCQQxIgQgjJfmk7jHIkNs -# VONfxUew9xW/Nbw04XbdfKGkwwy4vzYwNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQg -# naaQFcNJxsGJeEW6NYKtcMiPpCk722q+nCvSU5J55jswDQYJKoZIhvcNAQEBBQAE -# ggIAbAYmI9b3CGbOaJ/v+4qpnAXAnH5mrgFLKfMCV1JlWKR1eEbZjrTn9Ndl749P -# 5rt/ZnnJeMuuC4KYtAd/klVXYcsgzeOgal/OYnUcxxPXxWoCdJQZvNPLPHXCB0ni -# dVSQ4gMPyFP63kkZrCsfChQyUJIy934VTHJvpziOKSfqYEWQxZSRGpvaA6bArd7v -# LqDuGiqhaTbMeFLUhuCMh697jGyv5Bjn4DIISURUODpCb7cji9J/IsUbOhlKsNUv -# +SwU2tNRIaFRA02i1KxNz23Yp+vXwn/DtkQd0+2+eHhz/zQsp9VTrJ8ywV0+XHA2 -# /WnmQ1eb1r09fxv5q+JT63Qo+1JOrf4cYI1h0c/KRAjmKbTAcUjbmndXGu9uKTq9 -# owxbnrug8Ia/td1oXY4D9Cn6GP/TsP0xvramgy6Q2QLnNmI3T1AzBYTTHgOSU3+6 -# io6Gaq+mjh+9diVWXJpsFyLcRghLPG1VlkRiQzYcVygN8NTyVayj+8oJlcZR835F -# Gg1n1H34inU1ODJKfMNva1xD7tz5+5JAvv2NLshIstsrMX07mKlQipj/tq55X7Gh -# ST6VsAJ1EwN3hufImE2RKXQhG2OUkTPKoEOhgevxPpPJfyQHYk8ObJP9GhGlo6fq -# kw5+X1xI4K1MBjZPqWDqC120T4zIfUFKnkI+E4yqlFbK6C0= +# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z +# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ +# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0 +# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB +# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z +# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ +# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s +# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL +# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb +# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3 +# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c +# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx +# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0 +# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL +# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud +# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf +# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk +# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS +# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK +# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB +# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp +# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg +# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri +# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7 +# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5 +# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3 +# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H +# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G +# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ +# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0 +# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla +# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE +# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz +# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C +# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce +# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da +# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T +# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA +# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh +# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM +# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z +# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05 +# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY +# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP +# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T +# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD +# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG +# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY +# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj +# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV +# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU +# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN +# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry +# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL +# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf +# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh +# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh +# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV +# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j +# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH +# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC +# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l +# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW +# eE4wggd3MIIFX6ADAgECAhAHHxQbizANJfMU6yMM0NHdMA0GCSqGSIb3DQEBCwUA +# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE +# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz +# ODQgMjAyMSBDQTEwHhcNMjIwMTE3MDAwMDAwWhcNMjUwMTE1MjM1OTU5WjB8MQsw +# CQYDVQQGEwJVUzEPMA0GA1UECBMGT3JlZ29uMRIwEAYDVQQHEwlCZWF2ZXJ0b24x +# IzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMSMwIQYDVQQDExpQ +# eXRob24gU29mdHdhcmUgRm91bmRhdGlvbjCCAiIwDQYJKoZIhvcNAQEBBQADggIP +# ADCCAgoCggIBAKgc0BTT+iKbtK6f2mr9pNMUTcAJxKdsuOiSYgDFfwhjQy89koM7 +# uP+QV/gwx8MzEt3c9tLJvDccVWQ8H7mVsk/K+X+IufBLCgUi0GGAZUegEAeRlSXx +# xhYScr818ma8EvGIZdiSOhqjYc4KnfgfIS4RLtZSrDFG2tN16yS8skFa3IHyvWdb +# D9PvZ4iYNAS4pjYDRjT/9uzPZ4Pan+53xZIcDgjiTwOh8VGuppxcia6a7xCyKoOA +# GjvCyQsj5223v1/Ig7Dp9mGI+nh1E3IwmyTIIuVHyK6Lqu352diDY+iCMpk9Zanm +# SjmB+GMVs+H/gOiofjjtf6oz0ki3rb7sQ8fTnonIL9dyGTJ0ZFYKeb6BLA66d2GA +# LwxZhLe5WH4Np9HcyXHACkppsE6ynYjTOd7+jN1PRJahN1oERzTzEiV6nCO1M3U1 +# HbPTGyq52IMFSBM2/07WTJSbOeXjvYR7aUxK9/ZkJiacl2iZI7IWe7JKhHohqKuc +# eQNyOzxTakLcRkzynvIrk33R9YVqtB4L6wtFxhUjvDnQg16xot2KVPdfyPAWd81w +# tZADmrUtsZ9qG79x1hBdyOl4vUtVPECuyhCxaw+faVjumapPUnwo8ygflJJ74J+B +# Yxf6UuD7m8yzsfXWkdv52DjL74TxzuFTLHPyARWCSCAbzn3ZIly+qIqDAgMBAAGj +# ggIGMIICAjAfBgNVHSMEGDAWgBRoN+Drtjv4XxGG+/5hewiIZfROQjAdBgNVHQ4E +# FgQUt/1Teh2XDuUj2WW3siYWJgkZHA8wDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQM +# MAoGCCsGAQUFBwMDMIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6Ly9jcmwzLmRp +# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNI +# QTM4NDIwMjFDQTEuY3JsMFOgUaBPhk1odHRwOi8vY3JsNC5kaWdpY2VydC5jb20v +# RGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0Ex +# LmNybDA+BgNVHSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIBFhtodHRwOi8v +# d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwgZQGCCsGAQUFBwEBBIGHMIGEMCQGCCsGAQUF +# BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXAYIKwYBBQUHMAKGUGh0dHA6 +# Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWdu +# aW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZI +# hvcNAQELBQADggIBABxv4AeV/5ltkELHSC63fXAFYS5tadcWTiNc2rskrNLrfH1N +# s0vgSZFoQxYBFKI159E8oQQ1SKbTEubZ/B9kmHPhprHya08+VVzxC88pOEvz68nA +# 82oEM09584aILqYmj8Pj7h/kmZNzuEL7WiwFa/U1hX+XiWfLIJQsAHBla0i7QRF2 +# de8/VSF0XXFa2kBQ6aiTsiLyKPNbaNtbcucaUdn6vVUS5izWOXM95BSkFSKdE45O +# q3FForNJXjBvSCpwcP36WklaHL+aHu1upIhCTUkzTHMh8b86WmjRUqbrnvdyR2yd +# I5l1OqcMBjkpPpIV6wcc+KY/RH2xvVuuoHjlUjwq2bHiNoX+W1scCpnA8YTs2d50 +# jDHUgwUo+ciwpffH0Riq132NFmrH3r67VaN3TuBxjI8SIZM58WEDkbeoriDk3hxU +# 8ZWV7b8AW6oyVBGfM06UgkfMb58h+tJPrFx8VI/WLq1dTqMfZOm5cuclMnUHs2uq +# rRNtnV8UfidPBL4ZHkTcClQbCoz0UbLhkiDvIS00Dn+BBcxw/TKqVL4Oaz3bkMSs +# M46LciTeucHY9ExRVt3zy7i149sd+F4QozPqn7FrSVHXmem3r7bjyHTxOgqxRCVa +# 18Vtx7P/8bYSBeS+WHCKcliFCecspusCDSlnRUjZwyPdP0VHxaZg2unjHY3rMYIa +# tzCCGrMCAQEwfTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIElu +# Yy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJT +# QTQwOTYgU0hBMzg0IDIwMjEgQ0ExAhAHHxQbizANJfMU6yMM0NHdMA0GCWCGSAFl +# AwQCAQUAoIHIMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcC +# AQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCBnAZ6P7YvTwq0fbF62 +# o7E75R0LxsW5OtyYiFESQckLhjBcBgorBgEEAYI3AgEMMU4wTKBGgEQAQgB1AGkA +# bAB0ADoAIABSAGUAbABlAGEAcwBlAF8AdgAzAC4AMQAxAC4ANgBfADIAMAAyADMA +# MQAwADAAMgAuADAAMqECgAAwDQYJKoZIhvcNAQEBBQAEggIARo3eHChUNRTmOFDC +# zDmZrqy1W/YRZSLdkgUPjDp+9L59I9HC3TLxlV59PAX8Am4t3Sfz0wo8uDK7s4Xb +# IMLX7q1XLQQzGFZT6Y0uiH+jChwiSvAQMZ9Fniob/wQtSoOMafGvQfI8FIuEU7Z1 +# B3QQ1J+Uoz+3gEHgea6HdaHyP+OF4UOZDvKNPGd6FdkNFk643PdrO4ervVr8go80 +# XP48YM2P5Ce22UsTV6AWxXCMTMb9Yq+s317NPUMTHyfRQtwdjylF0hgIby3pqjT0 +# yPhSnhBS96UPWldXbRkvLpj2xe0hHjReaqm09iPBipS4/Yij54baMcyHoLaeXI43 +# l+9QDwaoVnGfVw/8a9mJaMgorhr8MkgHY283afyhw5dtUAJTBarQ/XEhWHuIzO5+ +# ZAI5tzcZ0i65tZlxFTyWWh9dY3vMI1hUbauGBsfUdPblqbXF1/Q28mlL65AGvnqU +# Wt8yoz9w4F0iPtgVzClttMR7/jO8eBMJhl86aHG7Ig6yDrrTQMvzngyzDQEv9S/W +# Qjy/vSp1h+z38g1oxd0B025UzI/l1I92enmN5JziHFukCBPrrC98aHk6PbLLwMqm +# omqWmq+zqAg43INr4C2j5vpiBncrhJH0SehUZ5oRLY46hXbbj4/Ev5yfameV54g3 +# xa9uUrEDT+Y+b9y+gBY9fYKhHdmhghdAMIIXPAYKKwYBBAGCNwMDATGCFywwghco +# BgkqhkiG9w0BBwKgghcZMIIXFQIBAzEPMA0GCWCGSAFlAwQCAQUAMHgGCyqGSIb3 +# DQEJEAEEoGkEZzBlAgEBBglghkgBhv1sBwEwMTANBglghkgBZQMEAgEFAAQgT3RV +# /MJk9H/R/k58DmHAEsiGSqwhcHvmZOgbetij8DACEQCBXfKkcwfpr4H+kfVj6/6x +# GA8yMDIzMTAwMjE1MTIxMlqgghMJMIIGwjCCBKqgAwIBAgIQBUSv85SdCDmmv9s/ +# X+VhFjANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGln +# aUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5 +# NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTIzMDcxNDAwMDAwMFoXDTM0MTAx +# MzIzNTk1OVowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu +# MSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMzCCAiIwDQYJKoZIhvcN +# AQEBBQADggIPADCCAgoCggIBAKNTRYcdg45brD5UsyPgz5/X5dLnXaEOCdwvSKOX +# ejsqnGfcYhVYwamTEafNqrJq3RApih5iY2nTWJw1cb86l+uUUI8cIOrHmjsvlmbj +# aedp/lvD1isgHMGXlLSlUIHyz8sHpjBoyoNC2vx/CSSUpIIa2mq62DvKXd4ZGIX7 +# ReoNYWyd/nFexAaaPPDFLnkPG2ZS48jWPl/aQ9OE9dDH9kgtXkV1lnX+3RChG4PB +# uOZSlbVH13gpOWvgeFmX40QrStWVzu8IF+qCZE3/I+PKhu60pCFkcOvV5aDaY7Mu +# 6QXuqvYk9R28mxyyt1/f8O52fTGZZUdVnUokL6wrl76f5P17cz4y7lI0+9S769Sg +# LDSb495uZBkHNwGRDxy1Uc2qTGaDiGhiu7xBG3gZbeTZD+BYQfvYsSzhUa+0rRUG +# FOpiCBPTaR58ZE2dD9/O0V6MqqtQFcmzyrzXxDtoRKOlO0L9c33u3Qr/eTQQfqZc +# ClhMAD6FaXXHg2TWdc2PEnZWpST618RrIbroHzSYLzrqawGw9/sqhux7UjipmAmh +# cbJsca8+uG+W1eEQE/5hRwqM/vC2x9XH3mwk8L9CgsqgcT2ckpMEtGlwJw1Pt7U2 +# 0clfCKRwo+wK8REuZODLIivK8SgTIUlRfgZm0zu++uuRONhRB8qUt+JQofM604qD +# y0B7AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAW +# BgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglg +# hkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYDVR0O +# BBYEFKW27xPn783QZKHVVqllMaPe1eNJMFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6 +# Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEy +# NTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUF +# BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6 +# Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZT +# SEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAIEa1t6g +# qbWYF7xwjU+KPGic2CX/yyzkzepdIpLsjCICqbjPgKjZ5+PF7SaCinEvGN1Ott5s +# 1+FgnCvt7T1IjrhrunxdvcJhN2hJd6PrkKoS1yeF844ektrCQDifXcigLiV4JZ0q +# BXqEKZi2V3mP2yZWK7Dzp703DNiYdk9WuVLCtp04qYHnbUFcjGnRuSvExnvPnPp4 +# 4pMadqJpddNQ5EQSviANnqlE0PjlSXcIWiHFtM+YlRpUurm8wWkZus8W8oM3NG6w +# QSbd3lqXTzON1I13fXVFoaVYJmoDRd7ZULVQjK9WvUzF4UbFKNOt50MAcN7MmJ4Z +# iQPq1JE3701S88lgIcRWR+3aEUuMMsOI5ljitts++V+wQtaP4xeR0arAVeOGv6wn +# LEHQmjNKqDbUuXKWfpd5OEhfysLcPTLfddY2Z1qJ+Panx+VPNTwAvb6cKmx5Adza +# ROY63jg7B145WPR8czFVoIARyxQMfq68/qTreWWqaNYiyjvrmoI1VygWy2nyMpqy +# 0tg6uLFGhmu6F/3Ed2wVbK6rr3M66ElGt9V/zLY4wNjsHPW2obhDLN9OTH0eaHDA +# dwrUAuBcYLso/zjlUlrWrBciI0707NMX+1Br/wd3H3GXREHJuEbTbDJ8WC9nR2Xl +# G3O2mflrLAZG70Ee8PBf4NvZrZCARK+AEEGKMIIGrjCCBJagAwIBAgIQBzY3tyRU +# fNhHrP0oZipeWzANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UE +# ChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYD +# VQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcN +# MzcwMzIyMjM1OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs +# IEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEy +# NTYgVGltZVN0YW1waW5nIENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC +# AgEAxoY1BkmzwT1ySVFVxyUDxPKRN6mXUaHW0oPRnkyibaCwzIP5WvYRoUQVQl+k +# iPNo+n3znIkLf50fng8zH1ATCyZzlm34V6gCff1DtITaEfFzsbPuK4CEiiIY3+va +# PcQXf6sZKz5C3GeO6lE98NZW1OcoLevTsbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RB +# idx8ald68Dd5n12sy+iEZLRS8nZH92GDGd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn +# 7w6lY2zkpsUdzTYNXNXmG6jBZHRAp8ByxbpOH7G1WE15/tePc5OsLDnipUjW8LAx +# E6lXKZYnLvWHpo9OdhVVJnCYJn+gGkcgQ+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB +# 3iIU2YIqx5K/oN7jPqJz+ucfWmyU8lKVEStYdEAoq3NDzt9KoRxrOMUp88qqlnNC +# aJ+2RrOdOqPVA+C/8KI8ykLcGEh/FDTP0kyr75s9/g64ZCr6dSgkQe1CvwWcZklS +# UPRR8zZJTYsg0ixXNXkrqPNFYLwjjVj33GHek/45wPmyMKVM1+mYSlg+0wOI/rOP +# 015LdhJRk8mMDDtbiiKowSYI+RQQEgN9XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXi +# YKNYCQEoAA6EVO7O6V3IXjASvUaetdN2udIOa5kM0jO0zbECAwEAAaOCAV0wggFZ +# MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCP +# nshvMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQE +# AwIBhjATBgNVHSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYB +# BQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0 +# cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5j +# cnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp +# Z2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJ +# YIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULh +# sBguEE0TzzBTzr8Y+8dQXeJLKftwig2qKWn8acHPHQfpPmDI2AvlXFvXbYf6hCAl +# NDFnzbYSlm/EUExiHQwIgqgWvalWzxVzjQEiJc6VaT9Hd/tydBTX/6tPiix6q4XN +# Q1/tYLaqT5Fmniye4Iqs5f2MvGQmh2ySvZ180HAKfO+ovHVPulr3qRCyXen/KFSJ +# 8NWKcXZl2szwcqMj+sAngkSumScbqyQeJsG33irr9p6xeZmBo1aGqwpFyd/EjaDn +# mPv7pp1yr8THwcFqcdnGE4AJxLafzYeHJLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsd +# CEkPlM05et3/JWOZJyw9P2un8WbDQc1PtkCbISFA0LcTJM3cHXg65J6t5TRxktcm +# a+Q4c6umAU+9Pzt4rUyt+8SVe+0KXzM5h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+ +# 8kaddSweJywm228Vex4Ziza4k9Tm8heZWcpw8De/mADfIBZPJ/tgZxahZrrdVcA6 +# KYawmKAr7ZVBtzrVFZgxtGIJDwq9gdkT/r+k0fNX2bwE+oLeMt8EifAAzV3C+dAj +# fwAL5HYCJtnwZXZCpimHCUcr5n8apIUP/JiW9lVUKx+A+sDyDivl1vupL0QVSucT +# Dh3bNzgaoSv27dZ8/DCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJ +# KoZIhvcNAQEMBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IElu +# YzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQg +# QXNzdXJlZCBJRCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1 +# OVowYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UE +# CxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBS +# b290IEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+Rd +# SjwwIjBpM+zCpyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20d +# q7J58soR0uRf1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7f +# gvMHhOZ0O21x4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRA +# X7F6Zu53yEioZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raR +# mECQecN4x7axxLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzU +# vK4bA3VdeGbZOjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2 +# mHY9WV1CdoeJl2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkr +# fsCUtNJhbesz2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaA +# sPvoZKYz0YkH4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxf +# jT/JvNNBERJb5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEe +# xcCPorF+CiaZ9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQF +# MAMBAf8wHQYDVR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaA +# FEXroq/0ksuCMS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcB +# AQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggr +# BgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNz +# dXJlZElEUm9vdENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5k +# aWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQK +# MAgwBgYEVR0gADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3 +# v1cHvZqsoYcs7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy +# 3iS8UgPITtAq3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cn +# RNTnf+hZqPC/Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3 +# WlxUjG/voVA9/HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2 +# zm8jLfR+cWojayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGC +# A3YwggNyAgEBMHcwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ +# bmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2 +# IFRpbWVTdGFtcGluZyBDQQIQBUSv85SdCDmmv9s/X+VhFjANBglghkgBZQMEAgEF +# AKCB0TAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8X +# DTIzMTAwMjE1MTIxMlowKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQUZvArMsLCyQ+C +# Xc6qisnGTxmcz0AwLwYJKoZIhvcNAQkEMSIEIBMBPS9EXwQp/+Tuq38xG0P43rKj +# C3ZpKJ9jY+S8zOnbMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEINL25G3tdCLM0dRA +# V2hBNm+CitpVmq4zFq9NGprUDHgoMA0GCSqGSIb3DQEBAQUABIICAIGNfN67oN8w +# pLMU/G4tubOFx4FZoRFIy8vNW63znQxW9rB87irzTBlNydTpKF+oq6ZFk86E0FoQ +# 91f4yUn66t2L15zV8WS06jG0HhMFsPgsqH+j2qMDvODVbpaVCdUuPHBobCkHvqAA +# urqI3a10q3ttCdoU3cLAr6d/eaSTqmDUd5QKZ+IBFNafQlqWf7YV7efbowXAoSRT +# nVazx/YIK3GIrvSB6bpkah+XIGDPzItfGZ9eO1nb+Zu7kXuYL5Xf9dkOFH3AUXGh +# Je3AiI2sZDUojTYxxfp1tEG3bpTV5ad57rvsgVJZ5g7X52UEuTmQ6UAQvp35UWld +# tWzgSUQxdUkWU4AcxUE9uScFMmmOV/2ea0bMnLkXHS7lJ+goQy3Ipyvnip++5xYR +# lkXSteqFpTyDKs/2hZeoBqxampm3PqBj1fRZJ916AFFYayEV8vchoCo9ZoEuL6Vu +# YL3tdH3Au/r+shc2AgvtgRdAhd4VeHpYUxaa94U9zQtV2wRjYV8/NuKk1CDOa7qA +# YJb9AZsmIat3IPyyZGt6Ywp/7M8qom0qcL3zKA75FGn3P4xVxAmm8KXAMkhfOFkE +# Pi82toPosJ66FzVJcjXOwa2c6C9I6VRHSKLaedwf8hbY57RvdQ2KIDMZ3ALBaJ2/ +# b7F8jx2hShogoDdEOzh+qotM2RllrIsT # SIG # End signature block diff --git a/Scripts/csv2ods b/Scripts/csv2ods new file mode 100644 index 0000000..fe2ff9e --- /dev/null +++ b/Scripts/csv2ods @@ -0,0 +1,229 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe +# -*- coding: utf-8 -*- +# Copyright (C) 2008 Agustin Henze -> agustinhenze at gmail.com +# +# This is free software. You may redistribute it under the terms +# of the Apache license and the GNU General Public License Version +# 2 or at your option any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Contributor(s): +# +# Søren Roug +# +# Oct 2014: Georges Khaznadar +# - ported to Python3 +# - imlemented the missing switch -c / --encoding, with an extra +# feature for POSIX platforms which can guess encoding. + +from odf.opendocument import OpenDocumentSpreadsheet +from odf.style import Style, TextProperties, ParagraphProperties, TableColumnProperties +from odf.text import P +from odf.table import Table, TableColumn, TableRow, TableCell +from optparse import OptionParser +import sys,csv,re, os, codecs + +if sys.version_info[0]==3: unicode=str + +if sys.version_info[0]==2: + class UTF8Recoder: + """ + Iterator that reads an encoded stream and reencodes the input to UTF-8 + """ + def __init__(self, f, encoding): + self.reader = codecs.getreader(encoding)(f) + + def __iter__(self): + return self + + def next(self): + return self.reader.next().encode("utf-8") + + class UnicodeReader: + """ + A CSV reader which will iterate over lines in the CSV file "f", + which is encoded in the given encoding. + """ + + def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds): + f = UTF8Recoder(f, encoding) + self.reader = csv.reader(f, dialect=dialect, **kwds) + + def next(self): + row = self.reader.next() + return [unicode(s, "utf-8") for s in row] + + def __iter__(self): + return self + + +def csvToOds( pathFileCSV, pathFileODS, tableName='table', + delimiter=',', quoting=csv.QUOTE_MINIMAL, + quotechar = '"', escapechar = None, + skipinitialspace = False, lineterminator = '\r\n', + encoding="utf-8"): + textdoc = OpenDocumentSpreadsheet() + # Create a style for the table content. One we can modify + # later in the word processor. + tablecontents = Style(name="Table Contents", family="paragraph") + tablecontents.addElement(ParagraphProperties(numberlines="false", linenumber="0")) + tablecontents.addElement(TextProperties(fontweight="bold")) + textdoc.styles.addElement(tablecontents) + + # Start the table + table = Table( name=tableName ) + + if sys.version_info[0]==3: + reader = csv.reader(open(pathFileCSV, encoding=encoding), + delimiter=delimiter, + quoting=quoting, + quotechar=quotechar, + escapechar=escapechar, + skipinitialspace=skipinitialspace, + lineterminator=lineterminator) + else: + reader = UnicodeReader(open(pathFileCSV), + encoding=encoding, + delimiter=delimiter, + quoting=quoting, + quotechar=quotechar, + escapechar=escapechar, + skipinitialspace=skipinitialspace, + lineterminator=lineterminator) + fltExp = re.compile('^\s*[-+]?\d+(\.\d+)?\s*$') + + for row in reader: + tr = TableRow() + table.addElement(tr) + for val in row: + if fltExp.match(val): + tc = TableCell(valuetype="float", value=val.strip()) + else: + tc = TableCell(valuetype="string") + tr.addElement(tc) + p = P(stylename=tablecontents,text=val) + tc.addElement(p) + + textdoc.spreadsheet.addElement(table) + textdoc.save( pathFileODS ) + +if __name__ == "__main__": + usage = "%prog -i file.csv -o file.ods -d" + parser = OptionParser(usage=usage, version="%prog 0.1") + parser.add_option('-i','--input', action='store', + dest='input', help='File input in csv') + parser.add_option('-o','--output', action='store', + dest='output', help='File output in ods') + parser.add_option('-d','--delimiter', action='store', + dest='delimiter', help='specifies a one-character string to use as the field separator. It defaults to ",".') + + parser.add_option('-c','--encoding', action='store', + dest='encoding', help='specifies the encoding the file csv. It defaults to utf-8') + + parser.add_option('-t','--table', action='store', + dest='tableName', help='The table name in the output file') + + parser.add_option('-s','--skipinitialspace', + dest='skipinitialspace', help='''specifies how to interpret whitespace which + immediately follows a delimiter. It defaults to False, which + means that whitespace immediately following a delimiter is part + of the following field.''') + + parser.add_option('-l','--lineterminator', action='store', + dest='lineterminator', help='''specifies the character sequence which should + terminate rows.''') + + parser.add_option('-q','--quoting', action='store', + dest='quoting', help='''It can take on any of the following module constants: + 0 = QUOTE_MINIMAL means only when required, for example, when a field contains either the quotechar or the delimiter + 1 = QUOTE_ALL means that quotes are always placed around fields. + 2 = QUOTE_NONNUMERIC means that quotes are always placed around fields which do not parse as integers or floating point numbers. + 3 = QUOTE_NONE means that quotes are never placed around fields. + It defaults is QUOTE_MINIMAL''') + + parser.add_option('-e','--escapechar', action='store', + dest='escapechar', help='''specifies a one-character string used to escape the delimiter when quoting is set to QUOTE_NONE.''') + + parser.add_option('-r','--quotechar', action='store', + dest='quotechar', help='''specifies a one-character string to use as the quoting character. It defaults to ".''') + + (options, args) = parser.parse_args() + + if options.input: + pathFileCSV = options.input + else: + parser.print_help() + exit( 0 ) + + if options.output: + pathFileODS = options.output + else: + parser.print_help() + exit( 0 ) + + if options.delimiter: + delimiter = options.delimiter + else: + delimiter = "," + + if options.skipinitialspace: + skipinitialspace = True + else: + skipinitialspace=False + + if options.lineterminator: + lineterminator = options.lineterminator + else: + lineterminator ="\r\n" + + if options.escapechar: + escapechar = options.escapechar + else: + escapechar=None + + if options.tableName: + tableName = options.tableName + else: + tableName = "table" + + if options.quotechar: + quotechar = options.quotechar + else: + quotechar = "\"" + + encoding = "utf-8" # default setting + ########################################################### + ## try to guess the encoding; this is implemented only with + ## POSIX platforms. Can it be improved? + output = os.popen('/usr/bin/file ' + pathFileCSV).read() + m=re.match(r'^.*: ([-a-zA-Z0-9]+) text$', output) + if m: + encoding=m.group(1) + if 'ISO-8859' in encoding: + encoding="latin-1" + else: + encoding="utf-8" + ############################################################ + # when the -c or --coding switch is used, it takes precedence + if options.encoding: + encoding = options.encoding + + csvToOds( pathFileCSV=unicode(pathFileCSV), + pathFileODS=unicode(pathFileODS), + delimiter=delimiter, skipinitialspace=skipinitialspace, + escapechar=escapechar, + lineterminator=unicode(lineterminator), + tableName=tableName, quotechar=quotechar, + encoding=encoding) + +# Local Variables: *** +# mode: python *** +# End: *** diff --git a/Scripts/django-admin.exe b/Scripts/django-admin.exe new file mode 100644 index 0000000..82c1302 Binary files /dev/null and b/Scripts/django-admin.exe differ diff --git a/Scripts/django-upgrade.exe b/Scripts/django-upgrade.exe new file mode 100644 index 0000000..084b2a3 Binary files /dev/null and b/Scripts/django-upgrade.exe differ diff --git a/Scripts/docutils.exe b/Scripts/docutils.exe new file mode 100644 index 0000000..56c71d8 Binary files /dev/null and b/Scripts/docutils.exe differ diff --git a/Scripts/f2py.exe b/Scripts/f2py.exe new file mode 100644 index 0000000..c6aa55d Binary files /dev/null and b/Scripts/f2py.exe differ diff --git a/Scripts/mailodf b/Scripts/mailodf new file mode 100644 index 0000000..4df2364 --- /dev/null +++ b/Scripts/mailodf @@ -0,0 +1,95 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe +# -*- coding: utf-8 -*- +# Copyright (C) 2006 Søren Roug, European Environment Agency +# +# This is free software. You may redistribute it under the terms +# of the Apache license and the GNU General Public License Version +# 2 or at your option any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Contributor(s): +# +from odf.odf2xhtml import ODF2XHTML +import zipfile +import sys, os, smtplib, getopt + +from email.mime.multipart import MIMEMultipart +from email.mime.nonmultipart import MIMENonMultipart +from email.mime.text import MIMEText +from email.encoders import encode_base64 + +if sys.version_info[0]==3: unicode=str + +def usage(): + sys.stderr.write("Usage: %s [-f from] [-s subject] inputfile recipients...\n" % sys.argv[0]) + +try: + opts, args = getopt.getopt(sys.argv[1:], "f:s:", ["from=", "subject="]) +except getopt.GetoptError: + usage() + sys.exit(2) + +fromaddr = os.getlogin() + "@" + os.getenv('HOSTNAME','localhost') +subject = None +for o, a in opts: + if o in ("-f", "--from"): + fromaddr = a + if o in ("-s", "--subject"): + subject = a + +if len(args) < 2: + usage() + sys.exit(2) + +suffices = { + 'wmf':('image','x-wmf'), + 'png':('image','png'), + 'gif':('image','gif'), + 'jpg':('image','jpeg'), + 'jpeg':('image','jpeg') + } + +msg = MIMEMultipart('related',type="text/html") +msg['From'] = fromaddr +# msg['Date'] = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime()) +msg['To'] = ','.join(args[1:]) +msg.preamble = 'This is a multi-part message in MIME format.' +msg.epilogue = '' +odhandler = ODF2XHTML() +result = odhandler.odf2xhtml(unicode(args[0])) +if subject: + msg['Subject'] = subject +else: + msg['Subject'] = odhandler.title +htmlpart = MIMEText(result,'html','us-ascii') +htmlpart['Content-Location'] = 'index.html' +msg.attach(htmlpart) +z = zipfile.ZipFile(unicode(args[0])) +for file in z.namelist(): + if file[0:9] == 'Pictures/': + suffix = file[file.rfind(".")+1:] + main,sub = suffices.get(suffix,('application','octet-stream')) + img = MIMENonMultipart(main,sub) + img.set_payload(z.read(file)) + img['Content-Location'] = "" + file + encode_base64(img) + msg.attach(img) +z.close() + +server = smtplib.SMTP('localhost') +#server.set_debuglevel(1) +server.sendmail(fromaddr, args[1:], msg.as_string()) +server.quit() + + +# Local Variables: *** +# mode: python *** +# End: *** diff --git a/Scripts/normalizer.exe b/Scripts/normalizer.exe new file mode 100644 index 0000000..091b0da Binary files /dev/null and b/Scripts/normalizer.exe differ diff --git a/Scripts/odf2mht b/Scripts/odf2mht new file mode 100644 index 0000000..3c86ace --- /dev/null +++ b/Scripts/odf2mht @@ -0,0 +1,72 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe +# -*- coding: utf-8 -*- +# Copyright (C) 2006 Søren Roug, European Environment Agency +# +# This is free software. You may redistribute it under the terms +# of the Apache license and the GNU General Public License Version +# 2 or at your option any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Contributor(s): +# +from __future__ import print_function +from odf.odf2xhtml import ODF2XHTML +import zipfile +import sys +#from time import gmtime, strftime + +from email.mime.multipart import MIMEMultipart +from email.mime.nonmultipart import MIMENonMultipart +from email.mime.text import MIMEText +from email import encoders + +if sys.version_info[0]==3: unicode=str + +if len(sys.argv) != 2: + sys.stderr.write("Usage: %s inputfile\n" % sys.argv[0]) + sys.exit(1) + +suffices = { + 'wmf':('image','x-wmf'), + 'png':('image','png'), + 'gif':('image','gif'), + 'jpg':('image','jpeg'), + 'jpeg':('image','jpeg') + } + +msg = MIMEMultipart('related',type="text/html") +# msg['Subject'] = 'Subject here' +# msg['From'] = '' +# msg['Date'] = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime()) +msg.preamble = 'This is a multi-part message in MIME format.' +msg.epilogue = '' +odhandler = ODF2XHTML() +result = odhandler.odf2xhtml(unicode(sys.argv[1])) +htmlpart = MIMEText(result,'html','us-ascii') +htmlpart['Content-Location'] = 'index.html' +msg.attach(htmlpart) +z = zipfile.ZipFile(sys.argv[1]) +for file in z.namelist(): + if file[0:9] == 'Pictures/': + suffix = file[file.rfind(".")+1:] + main,sub = suffices.get(suffix,('application','octet-stream')) + img = MIMENonMultipart(main,sub) + img.set_payload(z.read(file)) + img['Content-Location'] = "" + file + encoders.encode_base64(img) + msg.attach(img) +z.close() +print (msg.as_string()) + + +# Local Variables: *** +# mode: python *** +# End: *** diff --git a/Scripts/odf2xhtml b/Scripts/odf2xhtml new file mode 100644 index 0000000..3ac06e2 --- /dev/null +++ b/Scripts/odf2xhtml @@ -0,0 +1,59 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe +# -*- coding: utf-8 -*- +# Copyright (C) 2007 Søren Roug, European Environment Agency +# +# This is free software. You may redistribute it under the terms +# of the Apache license and the GNU General Public License Version +# 2 or at your option any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Contributor(s): +# +from odf.odf2xhtml import ODF2XHTML +import sys, getopt + +if sys.version_info[0]==3: unicode=str + +from io import StringIO + +def usage(): + sys.stderr.write("Usage: %s [-p] inputfile\n" % sys.argv[0]) + +try: + opts, args = getopt.getopt(sys.argv[1:], "ep", ["plain","embedable"]) +except getopt.GetoptError: + usage() + sys.exit(2) + +generatecss = True +embedable = False +for o, a in opts: + if o in ("-p", "--plain"): + generatecss = False + if o in ("-e", "--embedable"): + embedable = True + +if len(args) != 1: + usage() + sys.exit(2) + +odhandler = ODF2XHTML(generatecss, embedable) +try: + result = odhandler.odf2xhtml(unicode(args[0])) +except: + sys.stderr.write("Unable to open file %s or file is not OpenDocument\n" % args[0]) + sys.exit(1) +sys.stdout.write(result) + + +# Local Variables: *** +# mode: python *** +# End: *** diff --git a/Scripts/odf2xml b/Scripts/odf2xml new file mode 100644 index 0000000..eaafeec --- /dev/null +++ b/Scripts/odf2xml @@ -0,0 +1,81 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe +# -*- coding: utf-8 -*- +# Copyright (C) 2008 Søren Roug, European Environment Agency +# +# This is free software. You may redistribute it under the terms +# of the Apache license and the GNU General Public License Version +# 2 or at your option any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Contributor(s): +# +# + +# OpenDocument can be a complete office document in a single +# XML document. This script will create such a document. +import sys, getopt, base64 +from odf.opendocument import load +from odf.draw import Image, ObjectOle +from odf.style import BackgroundImage +from odf.text import ListLevelStyleImage +from odf.office import BinaryData + +if sys.version_info[0]==3: unicode=str + +def usage(): + sys.stderr.write("Usage: %s [-e] [-o outputfile] [inputfile]\n" % sys.argv[0]) + + +if __name__ == "__main__": + embedimage = False + try: + opts, args = getopt.getopt(sys.argv[1:], "o:e", ["output="]) + except getopt.GetoptError: + usage() + sys.exit(2) + + outputfile = '-' + + for o, a in opts: + if o in ("-o", "--output"): + outputfile = a + if o == '-e': + embedimage = True + + if len(args) > 1: + usage() + sys.exit(2) + if len(args) == 0: + d = load(sys.stdin) + else: + d = load(unicode(args[0])) + if embedimage: + images = d.getElementsByType(Image) + \ + d.getElementsByType(BackgroundImage) + \ + d.getElementsByType(ObjectOle) + \ + d.getElementsByType(ListLevelStyleImage) + for image in images: + href = image.getAttribute('href') + if href and href[:9] == "Pictures/": + p = d.Pictures[href] + bp = base64.encodestring(p[1]) + image.addElement(BinaryData(text=bp)) + image.removeAttribute('href') + xml = d.xml() + if outputfile == '-': + print (xml) + else: + open(outputfile,"wb").write(xml) + + +# Local Variables: *** +# mode: python *** +# End: *** diff --git a/Scripts/odfimgimport b/Scripts/odfimgimport new file mode 100644 index 0000000..d144731 --- /dev/null +++ b/Scripts/odfimgimport @@ -0,0 +1,190 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe +# -*- coding: utf-8 -*- +# Copyright (C) 2007-2009 Søren Roug, European Environment Agency +# +# This is free software. You may redistribute it under the terms +# of the Apache license and the GNU General Public License Version +# 2 or at your option any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Contributor(s): +# +from __future__ import print_function + +import zipfile, sys, getopt, mimetypes +try: + from urllib2 import urlopen, quote, unquote +except ImportError: + from urllib.request import urlopen, quote, unquote +try: + from urlparse import urlunsplit, urlsplit +except ImportError: + from urllib.parse import urlunsplit, urlsplit +from odf.opendocument import load +from odf.draw import Image + +if sys.version_info[0]==3: unicode=str + +#sys.tracebacklimit = 0 + +# Variable to count the number of retrieval failures +failures = 0 + +# Set to one if quiet behaviour is wanted +quiet = 0 + +# If set will write every url to import +verbose = 0 + +# Dictionary with new pictures. Key is original file path +# Item is newfilename +newpictures = {} +doc = None + +def importpicture(href): + """ Add the picture to the ZIP file + Returns the new path name to the file in the zip archive + If it is unable to import, then it returns the original href + Sideeffect: add line to manifest + """ + global doc, newpictures, failures, verbose + + # Check that it is not already in the manifest + if href in doc.Pictures: return href + + image = None + if verbose: print ("Importing", href, file=sys.stderr) + if href[:7] == "http://" or href[:8] == "https://" or href[:6] == "ftp://": + # There is a bug in urlopen: It can't open urls with non-ascii unicode + # characters. Convert to UTF-8 and then use percent encoding + try: + goodhref = href.encode('ascii') + except: + o = list(urlsplit(href)) + o[2] = quote(o[2].encode('utf-8')) + goodhref = urlunsplit(o) + if goodhref in newpictures: + if verbose: print ("already imported", file=sys.stderr) + return newpictures[goodhref] # Already imported + try: + f = urlopen(goodhref.decode("utf-8")) + image = f.read() + headers = f.info() + f.close() + # Get the mimetype from the headerlines + c_t = headers['Content-Type'].split(';')[0].strip() + if c_t: mediatype = c_t.split(';')[0].strip() + if verbose: print ("OK", file=sys.stderr) + except: + failures += 1 + if verbose: print ("failed", file=sys.stderr) + return href + # Remove query string + try: href= href[:href.rindex('?')] + except: pass + try: + lastslash = href[href.rindex('/'):] + ext = lastslash[lastslash.rindex('.'):] + except: ext = mimetypes.guess_extension(mediatype) + # Everything is a simple path. + else: + goodhref = href + if href[:3] == '../': + if directory is None: + goodhref = unquote(href[3:]) + else: + goodhref = unquote(directory + href[2:]) + if goodhref in newpictures: + if verbose: print ("already imported", file=sys.stderr) + return newpictures[goodhref] # Already imported + mediatype, encoding = mimetypes.guess_type(goodhref) + if mediatype is None: + mediatype = '' + try: ext = goodhref[goodhref.rindex('.'):] + except: ext='' + else: + ext = mimetypes.guess_extension(mediatype) + try: + image = file(goodhref).read() + if verbose: print ("OK", file=sys.stderr) + except: + failures += 1 + if verbose: print ("failed", file=sys.stderr) + return href + # If we have a picture to import, the image variable contains it + # and manifestfn, ext and mediatype has a value + if image: + manifestfn = doc.addPictureFromString(image, unicode(mediatype)) + newpictures[goodhref] = manifestfn + return manifestfn + + if verbose: print ("not imported", file=sys.stderr) + return href + +def exitwithusage(exitcode=2): + """ Print out usage information and exit """ + print ("Usage: %s [-q] [-v] [-o output] [inputfile]" % sys.argv[0], file=sys.stderr) + print ("\tInputfile must be OpenDocument format", file=sys.stderr) + sys.exit(exitcode) + +outputfile = None +writefile = True + +try: + opts, args = getopt.getopt(sys.argv[1:], "qvo:") +except getopt.GetoptError: + exitwithusage() + +for o, a in opts: + if o == "-o": + outputfile = a + writefile = True + if o == "-q": + quiet = 1 + if o == "-v": + verbose = 1 + +if len(args) == 0: + try: + doc = load(sys.stdin) + directory = None + except: + print ("Couldn't open OpenDocument file", file=sys.stderr) + exitwithusage() +else: + fn = unicode(args[0]) + if not zipfile.is_zipfile(fn): + exitwithusage() + dirinx = max(fn.rfind('\\'), fn.rfind('/')) + if dirinx >= 0: directory = fn[:dirinx] + else: directory = "." + doc = load(fn) + +for image in doc.getElementsByType(Image): + href = image.getAttribute('href') + newhref = importpicture(href) + image.setAttribute('href',newhref) + +if writefile: + if outputfile is None: + doc.save(fn) + else: + doc.save(unicode(outputfile)) + + +if quiet == 0 and failures > 0: + print ("Couldn't import %d image(s)" % failures, file=sys.stderr) +sys.exit( int(failures > 0) ) + + +# Local Variables: *** +# mode: python *** +# End: *** diff --git a/Scripts/odflint b/Scripts/odflint new file mode 100644 index 0000000..f39ca1e --- /dev/null +++ b/Scripts/odflint @@ -0,0 +1,216 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe +# -*- coding: utf-8 -*- +# Copyright (C) 2009 Søren Roug, European Environment Agency +# +# This is free software. You may redistribute it under the terms +# of the Apache license and the GNU General Public License Version +# 2 or at your option any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Contributor(s): +# +import zipfile +from xml.sax import make_parser,handler +from xml.sax.xmlreader import InputSource +import xml.sax.saxutils +import sys +from odf.opendocument import OpenDocument +from odf import element, grammar +from odf.namespaces import * +from odf.attrconverters import attrconverters, cnv_string + +from io import BytesIO + +if sys.version_info[0]==3: unicode=str + +extension_attributes = { + "OpenOffice.org" : { + (METANS,u'template'): ( + (XLINKNS,u'role'), + ), + (STYLENS,u'graphic-properties'): ( + (STYLENS,u'background-transparency'), + ), + (STYLENS,u'paragraph-properties'): ( + (TEXTNS,u'enable-numbering'), + (STYLENS,u'join-border'), + ), + (STYLENS,u'table-cell-properties'): ( + (STYLENS,u'writing-mode'), + ), + (STYLENS,u'table-row-properties'): ( + (STYLENS,u'keep-together'), + ), + }, + "KOffice" : { + (STYLENS,u'graphic-properties'): ( + (KOFFICENS,u'frame-behavior-on-new-page'), + ), + (DRAWNS,u'page'): ( + (KOFFICENS,u'name'), + ), + (PRESENTATIONNS,u'show-shape'): ( + (KOFFICENS,u'order-id'), + ), + (PRESENTATIONNS,u'hide-shape'): ( + (KOFFICENS,u'order-id'), + ), + (CHARTNS,u'legend'): ( + (KOFFICENS,u'title'), + ), + } +} + +printed_errors = [] + +def print_error(str): + if str not in printed_errors: + printed_errors.append(str) + print (str) + +def chop_arg(arg): + if len(arg) > 20: + return "%s..." % arg[0:20] + return arg + +def make_qname(tag): + return "%s:%s" % (nsdict.get(tag[0],tag[0]), tag[1]) + +def allowed_attributes(tag): + return grammar.allowed_attributes.get(tag) + + +class ODFElementHandler(handler.ContentHandler): + """ Extract headings from content.xml of an ODT file """ + def __init__(self, document): + self.doc = document + self.tagstack = [] + self.data = [] + self.currtag = None + + def characters(self, data): + self.data.append(data) + + def startElementNS(self, tag, qname, attrs): + """ Pseudo-create an element + """ + allowed_attrs = grammar.allowed_attributes.get(tag) + attrdict = {} + for (att,value) in attrs.items(): + prefix = nsdict.get(att[0],att[0]) + # Check if it is a known extension + notan_extension = True + for product, ext_attrs in extension_attributes.items(): + allowed_ext_attrs = ext_attrs.get(tag) + if allowed_ext_attrs and att in allowed_ext_attrs: + print_error("Warning: Attribute %s in element <%s> is illegal - %s extension" % ( make_qname(att), make_qname(tag), product)) + notan_extension = False + # Check if it is an allowed attribute + if notan_extension and allowed_attrs and att not in allowed_attrs: + print_error("Error: Attribute %s:%s is not allowed in element <%s>" % ( prefix, att[1], make_qname(tag))) + # Check the value + try: + convert = attrconverters.get(att, cnv_string) + convert(att, value, tag) + except ValueError as res: + print_error("Error: Bad value '%s' for attribute %s:%s in tag: <%s> - %s" % + (chop_arg(value), prefix, att[1], make_qname(tag), res)) + + self.tagstack.append(tag) + self.data = [] + # Check that the parent allows this child element + if tag not in ( (OFFICENS, 'document'), (OFFICENS, 'document-content'), (OFFICENS, 'document-styles'), + (OFFICENS, 'document-meta'), (OFFICENS, 'document-settings'), + (MANIFESTNS,'manifest')): + try: + parent = self.tagstack[-2] + allowed_children = grammar.allowed_children.get(parent) + except: + print_error("Error: This document starts with the wrong tag: <%s>" % make_qname(tag)) + allowed_children = None + if allowed_children and tag not in allowed_children: + print_error("Error: Element %s is not allowed in element %s" % ( make_qname(tag), make_qname(parent))) + # Test that all mandatory attributes have been added. + required = grammar.required_attributes.get(tag) + if required: + for r in required: + if attrs.get(r) is None: + print_error("Error: Required attribute missing: %s in <%s>" % (make_qname(r), make_qname(tag))) + + + def endElementNS(self, tag, qname): + self.currtag = self.tagstack.pop() + str = ''.join(self.data).strip() + # Check that only elements that can take text have text + # But only elements we know exist in grammar + if tag in grammar.allowed_children: + if str != '' and tag not in grammar.allows_text: + print_error("Error: %s does not allow text data" % make_qname(tag)) + self.data = [] + +class ODFDTDHandler(handler.DTDHandler): + def notationDecl(self, name, public_id, system_id): + """ Ignore DTDs """ + print_error("Warning: ODF doesn't use DOCTYPEs") + +def exitwithusage(exitcode=2): + """ print out usage information """ + sys.stderr.write("Usage: %s inputfile\n" % sys.argv[0]) + sys.stderr.write("\tInputfile must be OpenDocument format\n") + sys.exit(exitcode) + +def lint(odffile): + if not zipfile.is_zipfile(odffile): + print_error("Error: This is not a zipped file") + return + zfd = zipfile.ZipFile(odffile) + try: + mimetype = zfd.read('mimetype') + except: + mimetype='' + d = OpenDocument(unicode(mimetype)) + first = True + for zi in zfd.infolist(): + if first: + if zi.filename == 'mimetype': + if zi.compress_type != zipfile.ZIP_STORED: + print_error("Error: The 'mimetype' member must be stored - not deflated") + if zi.comment != "": + print_error("Error: The 'mimetype' member must not have extra header info") + else: + print_error("Warning: The first member in the archive should be the mimetype") + first = False + if zi.filename in ('META-INF/manifest.xml', 'content.xml', 'meta.xml', 'styles.xml', 'settings.xml'): + content = zfd.read(zi.filename) + parser = make_parser() + parser.setFeature(handler.feature_namespaces, True) + parser.setFeature(handler.feature_external_ges, False) + parser.setContentHandler(ODFElementHandler(d)) + dtdh = ODFDTDHandler() + parser.setDTDHandler(dtdh) + parser.setErrorHandler(handler.ErrorHandler()) + + inpsrc = InputSource() + if not isinstance(content, str): + content=content + inpsrc.setByteStream(BytesIO(content)) + parser.parse(inpsrc) + + +if len(sys.argv) != 2: + exitwithusage() +lint(unicode(sys.argv[1])) + + + +# Local Variables: *** +# mode: python *** +# End: *** diff --git a/Scripts/odfmeta b/Scripts/odfmeta new file mode 100644 index 0000000..24cab63 --- /dev/null +++ b/Scripts/odfmeta @@ -0,0 +1,266 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe +# -*- coding: utf-8 -*- +# Copyright (C) 2006-2009 Søren Roug, European Environment Agency +# +# This is free software. You may redistribute it under the terms +# of the Apache license and the GNU General Public License Version +# 2 or at your option any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Contributor(s): +# +import zipfile, time, sys, getopt, re +import xml.sax, xml.sax.saxutils +from odf.namespaces import TOOLSVERSION, OFFICENS, XLINKNS, DCNS, METANS +from io import BytesIO + +OUTENCODING="utf-8" + +whitespace = re.compile(r'\s+') + +fields = { +'title': (DCNS,u'title'), +'description': (DCNS,u'description'), +'subject': (DCNS,u'subject'), +'creator': (DCNS,u'creator'), +'date': (DCNS,u'date'), +'language': (DCNS,u'language'), +'generator': (METANS,u'generator'), +'initial-creator': (METANS,u'initial-creator'), +'keyword': (METANS,u'keyword'), +'editing-duration': (METANS,u'editing-duration'), +'editing-cycles': (METANS,u'editing-cycles'), +'printed-by': (METANS,u'printed-by'), +'print-date': (METANS,u'print-date'), +'creation-date': (METANS,u'creation-date'), +'user-defined': (METANS,u'user-defined'), +#'template': (METANS,u'template'), +} + +xfields = [] +Xfields = [] +addfields = {} +deletefields = {} +yieldfields = {} +showversion = None + +def exitwithusage(exitcode=2): + """ print out usage information """ + sys.stderr.write("Usage: %s [-cdlvV] [-xXaAI metafield]... [-o output] [inputfile]\n" % sys.argv[0]) + sys.stderr.write("\tInputfile must be OpenDocument format\n") + sys.exit(exitcode) + +def normalize(str): + """ + The normalize-space function returns the argument string with whitespace + normalized by stripping leading and trailing whitespace and replacing + sequences of whitespace characters by a single space. + """ + return whitespace.sub(' ', str).strip() + +class MetaCollector: + """ + The MetaCollector is a pseudo file object, that can temporarily ignore write-calls + It could probably be replaced with a StringIO object. + """ + def __init__(self): + self._content = [] + self.dowrite = True + + def write(self, str): + if self.dowrite: + self._content.append(str) + + def content(self): + return ''.join(self._content) + + +base = xml.sax.saxutils.XMLGenerator + +class odfmetaparser(base): + """ Parse a meta.xml file with an event-driven parser and replace elements. + It would probably be a cleaner approach to use a DOM based parser and + then manipulate in memory. + Small issue: Reorders elements + """ + version = 'Unknown' + + def __init__(self): + self._mimetype = '' + self.output = MetaCollector() + self._data = [] + self.seenfields = {} + base.__init__(self, self.output, OUTENCODING) + + def startElementNS(self, name, qname, attrs): + self._data = [] + field = name +# I can't modify the template until the tool replaces elements at the same +# location and not at the end +# if name == (METANS,u'template'): +# self._data = [attrs.get((XLINKNS,u'title'),'')] + if showversion and name == (OFFICENS,u'document-meta'): + if showversion == '-V': + print ("version:%s" % attrs.get((OFFICENS,u'version'),'Unknown').decode('utf-8')) + else: + print ("%s" % attrs.get((OFFICENS,u'version'),'Unknown').decode('utf-8')) + if name == (METANS,u'user-defined'): + field = attrs.get((METANS,u'name')) + if field in deletefields: + self.output.dowrite = False + elif field in yieldfields: + del addfields[field] + base.startElementNS(self, name, qname, attrs) + else: + base.startElementNS(self, name, qname, attrs) + self._tag = field + + def endElementNS(self, name, qname): + field = name + if name == (METANS,u'user-defined'): + field = self._tag + if name == (OFFICENS,u'meta'): + for k,v in addfields.items(): + if len(v) > 0: + if type(k) == type(''): + base.startElementNS(self,(METANS,u'user-defined'),None,{(METANS,u'name'):k}) + base.characters(self, v) + base.endElementNS(self, (METANS,u'user-defined'),None) + else: + base.startElementNS(self, k, None, {}) + base.characters(self, v) + base.endElementNS(self, k, None) + if name in xfields: + print ("%s" % self.data()) + if name in Xfields: + if isinstance(self._tag, tuple): + texttag = self._tag[1] + else: + texttag = self._tag + print ("%s:%s" % (texttag, self.data())) + if field in deletefields: + self.output.dowrite = True + else: + base.endElementNS(self, name, qname) + + def characters(self, content): + base.characters(self, content) + self._data.append(content) + + def meta(self): + return self.output.content() + + def data(self): + if usenormalize: + return normalize(''.join(self._data)) + else: + return ''.join(self._data) + +now = time.localtime()[:6] +outputfile = "-" +writemeta = False # Do we change any meta data? +usenormalize = False + +try: + opts, args = getopt.getopt(sys.argv[1:], "cdlvVI:A:a:o:x:X:") +except getopt.GetoptError: + exitwithusage() + +if len(opts) == 0: + opts = [ ('-l','') ] + +for o, a in opts: + if o in ('-a','-A','-I'): + writemeta = True + if a.find(":") >= 0: + k,v = a.split(":",1) + else: + k,v = (a, "") + if len(k) == 0: + exitwithusage() + k = fields.get(k,k) + addfields[k] = unicode(v,'utf-8') + if o == '-a': + yieldfields[k] = True + if o == '-I': + deletefields[k] = True + if o == '-d': + writemeta = True + addfields[(DCNS,u'date')] = "%04d-%02d-%02dT%02d:%02d:%02d" % now + deletefields[(DCNS,u'date')] = True + if o == '-c': + usenormalize = True + if o in ('-v', '-V'): + showversion = o + if o == '-l': + Xfields = fields.values() + if o == "-x": + xfields.append(fields.get(a,a)) + if o == "-X": + Xfields.append(fields.get(a,a)) + if o == "-o": + outputfile = a + +# The specification says we should change the element to our own, +# and must not export the original identifier. +if writemeta: + addfields[(METANS,u'generator')] = TOOLSVERSION + deletefields[(METANS,u'generator')] = True + +odfs = odfmetaparser() +parser = xml.sax.make_parser() +parser.setFeature(xml.sax.handler.feature_namespaces, 1) +parser.setContentHandler(odfs) + +if len(args) == 0: + zin = zipfile.ZipFile(sys.stdin,'r') +else: + if not zipfile.is_zipfile(args[0]): + exitwithusage() + zin = zipfile.ZipFile(args[0], 'r') + +try: + content = zin.read('meta.xml').decode('utf-8') +except: + sys.stderr.write("File has no meta data\n") + sys.exit(1) +parser.parse(BytesIO(content.encode('utf-8'))) + +if writemeta: + if outputfile == '-': + if sys.stdout.isatty(): + sys.stderr.write("Won't write ODF file to terminal\n") + sys.exit(1) + zout = zipfile.ZipFile(sys.stdout,"w") + else: + zout = zipfile.ZipFile(outputfile,"w") + + + + # Loop through the input zipfile and copy the content to the output until we + # get to the meta.xml. Then substitute. + for zinfo in zin.infolist(): + if zinfo.filename == "meta.xml": + # Write meta + zi = zipfile.ZipInfo("meta.xml", now) + zi.compress_type = zipfile.ZIP_DEFLATED + zout.writestr(zi,odfs.meta() ) + else: + payload = zin.read(zinfo.filename) + zout.writestr(zinfo, payload) + + zout.close() +zin.close() + + +# Local Variables: *** +# mode: python *** +# End: *** diff --git a/Scripts/odfoutline b/Scripts/odfoutline new file mode 100644 index 0000000..d13de93 --- /dev/null +++ b/Scripts/odfoutline @@ -0,0 +1,144 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe +# -*- coding: utf-8 -*- +# Copyright (C) 2006 Søren Roug, European Environment Agency +# +# This is free software. You may redistribute it under the terms +# of the Apache license and the GNU General Public License Version +# 2 or at your option any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Contributor(s): +# +from __future__ import print_function +import zipfile +from xml.sax import make_parser,handler +from xml.sax.xmlreader import InputSource +import xml.sax.saxutils +import sys +from odf.namespaces import TEXTNS, TABLENS, DRAWNS + +try: + from cStringIO import StringIO +except ImportError: + from io import StringIO + + +def getxmlpart(odffile, xmlfile): + """ Get the content out of the ODT file""" + z = zipfile.ZipFile(odffile) + content = z.read(xmlfile) + z.close() + return content + + + +# +# Extract headings from content.xml +# +class ODTHeadingHandler(handler.ContentHandler): + """ Extract headings from content.xml of an ODT file """ + def __init__(self, eater): + self.r = eater + self.data = [] + self.level = 0 + + def characters(self, data): + self.data.append(data) + + def startElementNS(self, tag, qname, attrs): + if tag == (TEXTNS, 'h'): + self.level = 0 + for (att,value) in attrs.items(): + if att == (TEXTNS, 'outline-level'): + self.level = int(value) + self.data = [] + + def endElementNS(self, tag, qname): + if tag == (TEXTNS, 'h'): + str = ''.join(self.data) + self.data = [] + self.r.append("%d%*s%s" % (self.level, self.level, '', str)) + +class ODTSheetHandler(handler.ContentHandler): + """ Extract sheet names from content.xml of an ODS file """ + def __init__(self, eater): + self.r = eater + + def startElementNS(self, tag, qname, attrs): + if tag == (TABLENS, 'table'): + sheetname = attrs.get((TABLENS, 'name')) + if sheetname: + self.r.append(sheetname) + +class ODTSlideHandler(handler.ContentHandler): + """ Extract headings from content.xml of an ODT file """ + def __init__(self, eater): + self.r = eater + self.data = [] + self.pagenum = 0 + + def characters(self, data): + self.data.append(data) + + def startElementNS(self, tag, qname, attrs): + if tag == (DRAWNS, 'page'): + self.pagenum = self.pagenum + 1 + self.r.append("SLIDE %d: %s" % ( self.pagenum, attrs.get((DRAWNS, 'name'),''))) + if tag == (TEXTNS, 'p'): + self.data = [] + + def endElementNS(self, tag, qname): + if tag == (TEXTNS, 'p'): + str = ''.join(self.data) + self.data = [] + if len(str) > 0: + self.r.append(" " + str) + +def odtheadings(odtfile): + mimetype = getxmlpart(odtfile,'mimetype') + content = getxmlpart(odtfile,'content.xml') + lines = [] + parser = make_parser() + parser.setFeature(handler.feature_namespaces, 1) + if not isinstance(mimetype, str): + mimetype=mimetype.decode("utf-8") + if mimetype in ('application/vnd.oasis.opendocument.text', + 'application/vnd.oasis.opendocument.text-template'): + parser.setContentHandler(ODTHeadingHandler(lines)) + elif mimetype in ('application/vnd.oasis.opendocument.spreadsheet', + 'application/vnd.oasis.opendocument.spreadsheet-template'): + parser.setContentHandler(ODTSheetHandler(lines)) + elif mimetype in ('application/vnd.oasis.opendocument.presentation' + 'application/vnd.oasis.opendocument.presentation-template'): + parser.setContentHandler(ODTSlideHandler(lines)) + else: + print ("Unsupported fileformat") + sys.exit(2) + parser.setErrorHandler(handler.ErrorHandler()) + + inpsrc = InputSource() + if not isinstance(content, str): + content=content.decode("utf-8") + inpsrc.setByteStream(StringIO(content)) + parser.parse(inpsrc) + return lines + + +if __name__ == "__main__": + filler = " " + for heading in odtheadings(sys.argv[1]): + print (heading) + + + +# Local Variables: *** +# mode: python *** +# End: *** diff --git a/Scripts/odfuserfield b/Scripts/odfuserfield new file mode 100644 index 0000000..5765cac --- /dev/null +++ b/Scripts/odfuserfield @@ -0,0 +1,101 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe +# -*- coding: utf-8 -*- +# Copyright (C) 2006-2007 Søren Roug, European Environment Agency +# +# This is free software. You may redistribute it under the terms +# of the Apache license and the GNU General Public License Version +# 2 or at your option any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Contributor(s): Michael Howitz, gocept gmbh & co. kg + +import sys +import getopt + +import odf.userfield + +if sys.version_info[0]==3: unicode=str + +listfields = False +Listfields = False +xfields = [] +Xfields = [] +setfields = {} +outputfile = None +inputfile = None + + +def exitwithusage(exitcode=2): + """ print out usage information """ + sys.stderr.write("Usage: %s [-lL] [-xX metafield] [-s metafield:value]... " + "[-o output] [inputfile]\n" % sys.argv[0]) + sys.stderr.write("\tInputfile must be OpenDocument format\n") + sys.exit(exitcode) + + +try: + opts, args = getopt.getopt(sys.argv[1:], "lLs:o:x:X:") +except getopt.GetoptError: + exitwithusage() + +if len(opts) == 0: + exitwithusage() + +for o, a in opts: + if o == '-s': + if a.find(":") >= 0: + k,v = a.split(":",1) + else: + k,v = (a, "") + if len(k) == 0: + exitwithusage() + setfields[unicode(k)] = unicode(v) + if o == '-l': + listfields = True + Listfields = False + if o == '-L': + Listfields = True + listfields = False + if o == "-x": + xfields.append(unicode(a)) + if o == "-X": + Xfields.append(unicode(a)) + if o == "-o": + outputfile = unicode(a) + +if len(args) != 0: + inputfile = unicode(args[0]) + +user_fields = odf.userfield.UserFields(inputfile, outputfile) + +if xfields: + for value in user_fields.list_values(xfields): + print (value) + +if Listfields or Xfields: + if Listfields: + Xfields = None + for field_name, value_type, value in user_fields.list_fields_and_values( + Xfields): + print ("%s#%s:%s" % (field_name, value_type, value)) + +if listfields: + for value in user_fields.list_fields(): + print (value) + +if setfields: + user_fields.update(setfields) + + + +# Local Variables: *** +# mode: python *** +# End: *** diff --git a/Scripts/pip.exe b/Scripts/pip.exe index 9c7f0b1..7dfe5af 100644 Binary files a/Scripts/pip.exe and b/Scripts/pip.exe differ diff --git a/Scripts/pip3.10.exe b/Scripts/pip3.10.exe deleted file mode 100644 index 9c7f0b1..0000000 Binary files a/Scripts/pip3.10.exe and /dev/null differ diff --git a/Scripts/pip3.12.exe b/Scripts/pip3.12.exe new file mode 100644 index 0000000..7dfe5af Binary files /dev/null and b/Scripts/pip3.12.exe differ diff --git a/Scripts/pip3.exe b/Scripts/pip3.exe index 9c7f0b1..7dfe5af 100644 Binary files a/Scripts/pip3.exe and b/Scripts/pip3.exe differ diff --git a/Scripts/python.exe b/Scripts/python.exe index 1466f06..5da7da6 100644 Binary files a/Scripts/python.exe and b/Scripts/python.exe differ diff --git a/Scripts/pythonw.exe b/Scripts/pythonw.exe index 569e3e4..9af4148 100644 Binary files a/Scripts/pythonw.exe and b/Scripts/pythonw.exe differ diff --git a/Scripts/rst2html.py b/Scripts/rst2html.py new file mode 100644 index 0000000..2e153a1 --- /dev/null +++ b/Scripts/rst2html.py @@ -0,0 +1,23 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe + +# $Id: rst2html.py 9115 2022-07-28 17:06:24Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing HTML. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except Exception: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates (X)HTML documents from standalone reStructuredText ' + 'sources. ' + default_description) + +publish_cmdline(writer_name='html', description=description) diff --git a/Scripts/rst2html4.py b/Scripts/rst2html4.py new file mode 100644 index 0000000..6f1d040 --- /dev/null +++ b/Scripts/rst2html4.py @@ -0,0 +1,26 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe + +# $Id: rst2html4.py 9115 2022-07-28 17:06:24Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing (X)HTML. + +The output conforms to XHTML 1.0 transitional +and almost to HTML 4.01 transitional (except for closing empty tags). +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except Exception: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates (X)HTML documents from standalone reStructuredText ' + 'sources. ' + default_description) + +publish_cmdline(writer_name='html4', description=description) diff --git a/Scripts/rst2html5.py b/Scripts/rst2html5.py new file mode 100644 index 0000000..c6097cc --- /dev/null +++ b/Scripts/rst2html5.py @@ -0,0 +1,33 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe +# :Copyright: © 2015 Günter Milde. +# :License: Released under the terms of the `2-Clause BSD license`_, in short: +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. +# This file is offered as-is, without any warranty. +# +# .. _2-Clause BSD license: https://opensource.org/licenses/BSD-2-Clause +# +# Revision: $Revision: 9021 $ +# Date: $Date: 2022-03-04 16:54:22 +0100 (Fr, 04. Mär 2022) $ + +""" +A minimal front end to the Docutils Publisher, producing HTML 5 documents. + +The output is also valid XML. +""" + +try: + import locale # module missing in Jython + locale.setlocale(locale.LC_ALL, '') +except locale.Error: + pass + +from docutils.core import publish_cmdline, default_description + +description = ('Generates HTML5 documents from standalone ' + 'reStructuredText sources.\n' + + default_description) + +publish_cmdline(writer_name='html5', description=description) diff --git a/Scripts/rst2latex.py b/Scripts/rst2latex.py new file mode 100644 index 0000000..a166d04 --- /dev/null +++ b/Scripts/rst2latex.py @@ -0,0 +1,26 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe + +# $Id: rst2latex.py 9115 2022-07-28 17:06:24Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing LaTeX. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except Exception: + pass + +from docutils.core import publish_cmdline + +description = ('Generates LaTeX documents from standalone reStructuredText ' + 'sources. ' + 'Reads from (default is stdin) and writes to ' + ' (default is stdout). See ' + ' for ' + 'the full reference.') + +publish_cmdline(writer_name='latex', description=description) diff --git a/Scripts/rst2man.py b/Scripts/rst2man.py new file mode 100644 index 0000000..da57fc9 --- /dev/null +++ b/Scripts/rst2man.py @@ -0,0 +1,27 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe + +# Author: +# Contact: grubert@users.sf.net +# Copyright: This module has been placed in the public domain. + +""" +man.py +====== + +This module provides a simple command line interface that uses the +man page writer to output from ReStructuredText source. +""" + +import locale +try: + locale.setlocale(locale.LC_ALL, '') +except Exception: + pass + +from docutils.core import publish_cmdline, default_description +from docutils.writers import manpage + +description = ("Generates plain unix manual documents. " + + default_description) + +publish_cmdline(writer=manpage.Writer(), description=description) diff --git a/Scripts/rst2odt.py b/Scripts/rst2odt.py new file mode 100644 index 0000000..cf34fcb --- /dev/null +++ b/Scripts/rst2odt.py @@ -0,0 +1,28 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe + +# $Id: rst2odt.py 9115 2022-07-28 17:06:24Z milde $ +# Author: Dave Kuhlman +# Copyright: This module has been placed in the public domain. + +""" +A front end to the Docutils Publisher, producing OpenOffice documents. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except Exception: + pass + +from docutils.core import publish_cmdline_to_binary, default_description +from docutils.writers.odf_odt import Writer, Reader + + +description = ('Generates OpenDocument/OpenOffice/ODF documents from ' + 'standalone reStructuredText sources. ' + default_description) + + +writer = Writer() +reader = Reader() +output = publish_cmdline_to_binary(reader=reader, writer=writer, + description=description) diff --git a/Scripts/rst2odt_prepstyles.py b/Scripts/rst2odt_prepstyles.py new file mode 100644 index 0000000..8ead88f --- /dev/null +++ b/Scripts/rst2odt_prepstyles.py @@ -0,0 +1,20 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe + +# Copyright: This module has been placed in the public domain. + +""" +Adapt a word-processor-generated styles.odt for odtwriter use: + +Drop page size specifications from styles.xml in STYLE_FILE.odt. +See https://docutils.sourceforge.io/docs/user/odt.html#page-size + +Provisional backwards compatibility stub (to be removed in Docutils >= 0.21). + +The actual code moved to the "docutils" library package and can be started +with ``python -m docutils.writers.odf_odt.prepstyles``. +""" + +from docutils.writers.odf_odt import prepstyles + +if __name__ == '__main__': + prepstyles.main() diff --git a/Scripts/rst2pseudoxml.py b/Scripts/rst2pseudoxml.py new file mode 100644 index 0000000..3ff09de --- /dev/null +++ b/Scripts/rst2pseudoxml.py @@ -0,0 +1,23 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe + +# $Id: rst2pseudoxml.py 9115 2022-07-28 17:06:24Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing pseudo-XML. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except Exception: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates pseudo-XML from standalone reStructuredText ' + 'sources (for testing purposes). ' + default_description) + +publish_cmdline(description=description) diff --git a/Scripts/rst2s5.py b/Scripts/rst2s5.py new file mode 100644 index 0000000..b8c501e --- /dev/null +++ b/Scripts/rst2s5.py @@ -0,0 +1,24 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe + +# $Id: rst2s5.py 9115 2022-07-28 17:06:24Z milde $ +# Author: Chris Liechti +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing HTML slides using +the S5 template system. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except Exception: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates S5 (X)HTML slideshow documents from standalone ' + 'reStructuredText sources. ' + default_description) + +publish_cmdline(writer_name='s5', description=description) diff --git a/Scripts/rst2xetex.py b/Scripts/rst2xetex.py new file mode 100644 index 0000000..1f6ed2d --- /dev/null +++ b/Scripts/rst2xetex.py @@ -0,0 +1,27 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe + +# $Id: rst2xetex.py 9115 2022-07-28 17:06:24Z milde $ +# Author: Guenter Milde +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing Lua/XeLaTeX code. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except Exception: + pass + +from docutils.core import publish_cmdline + +description = ('Generates LaTeX documents from standalone reStructuredText ' + 'sources for compilation with the Unicode-aware TeX variants ' + 'XeLaTeX or LuaLaTeX. ' + 'Reads from (default is stdin) and writes to ' + ' (default is stdout). See ' + ' for ' + 'the full reference.') + +publish_cmdline(writer_name='xetex', description=description) diff --git a/Scripts/rst2xml.py b/Scripts/rst2xml.py new file mode 100644 index 0000000..2d4e697 --- /dev/null +++ b/Scripts/rst2xml.py @@ -0,0 +1,23 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe + +# $Id: rst2xml.py 9115 2022-07-28 17:06:24Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing Docutils XML. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except Exception: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates Docutils-native XML from standalone ' + 'reStructuredText sources. ' + default_description) + +publish_cmdline(writer_name='xml', description=description) diff --git a/Scripts/rstpep2html.py b/Scripts/rstpep2html.py new file mode 100644 index 0000000..4cdc042 --- /dev/null +++ b/Scripts/rstpep2html.py @@ -0,0 +1,25 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe + +# $Id: rstpep2html.py 9115 2022-07-28 17:06:24Z milde $ +# Author: David Goodger +# Copyright: This module has been placed in the public domain. + +""" +A minimal front end to the Docutils Publisher, producing HTML from PEP +(Python Enhancement Proposal) documents. +""" + +try: + import locale + locale.setlocale(locale.LC_ALL, '') +except Exception: + pass + +from docutils.core import publish_cmdline, default_description + + +description = ('Generates (X)HTML from reStructuredText-format PEP files. ' + + default_description) + +publish_cmdline(reader_name='pep', writer_name='pep_html', + description=description) diff --git a/Scripts/runxlrd.py b/Scripts/runxlrd.py new file mode 100644 index 0000000..9010b3d --- /dev/null +++ b/Scripts/runxlrd.py @@ -0,0 +1,410 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe +# Copyright (c) 2005-2012 Stephen John Machin, Lingfo Pty Ltd +# This script is part of the xlrd package, which is released under a +# BSD-style licence. + +from __future__ import print_function + +cmd_doc = """ +Commands: + +2rows Print the contents of first and last row in each sheet +3rows Print the contents of first, second and last row in each sheet +bench Same as "show", but doesn't print -- for profiling +biff_count[1] Print a count of each type of BIFF record in the file +biff_dump[1] Print a dump (char and hex) of the BIFF records in the file +fonts hdr + print a dump of all font objects +hdr Mini-overview of file (no per-sheet information) +hotshot Do a hotshot profile run e.g. ... -f1 hotshot bench bigfile*.xls +labels Dump of sheet.col_label_ranges and ...row... for each sheet +name_dump Dump of each object in book.name_obj_list +names Print brief information for each NAME record +ov Overview of file +profile Like "hotshot", but uses cProfile +show Print the contents of all rows in each sheet +version[0] Print versions of xlrd and Python and exit +xfc Print "XF counts" and cell-type counts -- see code for details + +[0] means no file arg +[1] means only one file arg i.e. no glob.glob pattern +""" + +options = None +if __name__ == "__main__": + import xlrd + import sys + import time + import glob + import traceback + import gc + + from xlrd.timemachine import xrange, REPR + + + class LogHandler(object): + + def __init__(self, logfileobj): + self.logfileobj = logfileobj + self.fileheading = None + self.shown = 0 + + def setfileheading(self, fileheading): + self.fileheading = fileheading + self.shown = 0 + + def write(self, text): + if self.fileheading and not self.shown: + self.logfileobj.write(self.fileheading) + self.shown = 1 + self.logfileobj.write(text) + + null_cell = xlrd.empty_cell + + def show_row(bk, sh, rowx, colrange, printit): + if bk.ragged_rows: + colrange = range(sh.row_len(rowx)) + if not colrange: return + if printit: print() + if bk.formatting_info: + for colx, ty, val, cxfx in get_row_data(bk, sh, rowx, colrange): + if printit: + print("cell %s%d: type=%d, data: %r, xfx: %s" + % (xlrd.colname(colx), rowx+1, ty, val, cxfx)) + else: + for colx, ty, val, _unused in get_row_data(bk, sh, rowx, colrange): + if printit: + print("cell %s%d: type=%d, data: %r" % (xlrd.colname(colx), rowx+1, ty, val)) + + def get_row_data(bk, sh, rowx, colrange): + result = [] + dmode = bk.datemode + ctys = sh.row_types(rowx) + cvals = sh.row_values(rowx) + for colx in colrange: + cty = ctys[colx] + cval = cvals[colx] + if bk.formatting_info: + cxfx = str(sh.cell_xf_index(rowx, colx)) + else: + cxfx = '' + if cty == xlrd.XL_CELL_DATE: + try: + showval = xlrd.xldate_as_tuple(cval, dmode) + except xlrd.XLDateError as e: + showval = "%s:%s" % (type(e).__name__, e) + cty = xlrd.XL_CELL_ERROR + elif cty == xlrd.XL_CELL_ERROR: + showval = xlrd.error_text_from_code.get(cval, '' % cval) + else: + showval = cval + result.append((colx, cty, showval, cxfx)) + return result + + def bk_header(bk): + print() + print("BIFF version: %s; datemode: %s" + % (xlrd.biff_text_from_num[bk.biff_version], bk.datemode)) + print("codepage: %r (encoding: %s); countries: %r" + % (bk.codepage, bk.encoding, bk.countries)) + print("Last saved by: %r" % bk.user_name) + print("Number of data sheets: %d" % bk.nsheets) + print("Use mmap: %d; Formatting: %d; On demand: %d" + % (bk.use_mmap, bk.formatting_info, bk.on_demand)) + print("Ragged rows: %d" % bk.ragged_rows) + if bk.formatting_info: + print("FORMATs: %d, FONTs: %d, XFs: %d" + % (len(bk.format_list), len(bk.font_list), len(bk.xf_list))) + if not options.suppress_timing: + print("Load time: %.2f seconds (stage 1) %.2f seconds (stage 2)" + % (bk.load_time_stage_1, bk.load_time_stage_2)) + print() + + def show_fonts(bk): + print("Fonts:") + for x in xrange(len(bk.font_list)): + font = bk.font_list[x] + font.dump(header='== Index %d ==' % x, indent=4) + + def show_names(bk, dump=0): + bk_header(bk) + if bk.biff_version < 50: + print("Names not extracted in this BIFF version") + return + nlist = bk.name_obj_list + print("Name list: %d entries" % len(nlist)) + for nobj in nlist: + if dump: + nobj.dump(sys.stdout, + header="\n=== Dump of name_obj_list[%d] ===" % nobj.name_index) + else: + print("[%d]\tName:%r macro:%r scope:%d\n\tresult:%r\n" + % (nobj.name_index, nobj.name, nobj.macro, nobj.scope, nobj.result)) + + def print_labels(sh, labs, title): + if not labs:return + for rlo, rhi, clo, chi in labs: + print("%s label range %s:%s contains:" + % (title, xlrd.cellname(rlo, clo), xlrd.cellname(rhi-1, chi-1))) + for rx in xrange(rlo, rhi): + for cx in xrange(clo, chi): + print(" %s: %r" % (xlrd.cellname(rx, cx), sh.cell_value(rx, cx))) + + def show_labels(bk): + # bk_header(bk) + hdr = 0 + for shx in range(bk.nsheets): + sh = bk.sheet_by_index(shx) + clabs = sh.col_label_ranges + rlabs = sh.row_label_ranges + if clabs or rlabs: + if not hdr: + bk_header(bk) + hdr = 1 + print("sheet %d: name = %r; nrows = %d; ncols = %d" % + (shx, sh.name, sh.nrows, sh.ncols)) + print_labels(sh, clabs, 'Col') + print_labels(sh, rlabs, 'Row') + if bk.on_demand: bk.unload_sheet(shx) + + def show(bk, nshow=65535, printit=1): + bk_header(bk) + if 0: + rclist = xlrd.sheet.rc_stats.items() + rclist = sorted(rclist) + print("rc stats") + for k, v in rclist: + print("0x%04x %7d" % (k, v)) + if options.onesheet: + try: + shx = int(options.onesheet) + except ValueError: + shx = bk.sheet_by_name(options.onesheet).number + shxrange = [shx] + else: + shxrange = range(bk.nsheets) + # print("shxrange", list(shxrange)) + for shx in shxrange: + sh = bk.sheet_by_index(shx) + nrows, ncols = sh.nrows, sh.ncols + colrange = range(ncols) + anshow = min(nshow, nrows) + print("sheet %d: name = %s; nrows = %d; ncols = %d" % + (shx, REPR(sh.name), sh.nrows, sh.ncols)) + if nrows and ncols: + # Beat the bounds + for rowx in xrange(nrows): + nc = sh.row_len(rowx) + if nc: + sh.row_types(rowx)[nc-1] + sh.row_values(rowx)[nc-1] + sh.cell(rowx, nc-1) + for rowx in xrange(anshow-1): + if not printit and rowx % 10000 == 1 and rowx > 1: + print("done %d rows" % (rowx-1,)) + show_row(bk, sh, rowx, colrange, printit) + if anshow and nrows: + show_row(bk, sh, nrows-1, colrange, printit) + print() + if bk.on_demand: bk.unload_sheet(shx) + + def count_xfs(bk): + bk_header(bk) + for shx in range(bk.nsheets): + sh = bk.sheet_by_index(shx) + nrows = sh.nrows + print("sheet %d: name = %r; nrows = %d; ncols = %d" % + (shx, sh.name, sh.nrows, sh.ncols)) + # Access all xfindexes to force gathering stats + type_stats = [0, 0, 0, 0, 0, 0, 0] + for rowx in xrange(nrows): + for colx in xrange(sh.row_len(rowx)): + xfx = sh.cell_xf_index(rowx, colx) + assert xfx >= 0 + cty = sh.cell_type(rowx, colx) + type_stats[cty] += 1 + print("XF stats", sh._xf_index_stats) + print("type stats", type_stats) + print() + if bk.on_demand: bk.unload_sheet(shx) + + def main(cmd_args): + import optparse + global options + usage = "\n%prog [options] command [input-file-patterns]\n" + cmd_doc + oparser = optparse.OptionParser(usage) + oparser.add_option( + "-l", "--logfilename", + default="", + help="contains error messages") + oparser.add_option( + "-v", "--verbosity", + type="int", default=0, + help="level of information and diagnostics provided") + oparser.add_option( + "-m", "--mmap", + type="int", default=-1, + help="1: use mmap; 0: don't use mmap; -1: accept heuristic") + oparser.add_option( + "-e", "--encoding", + default="", + help="encoding override") + oparser.add_option( + "-f", "--formatting", + type="int", default=0, + help="0 (default): no fmt info\n" + "1: fmt info (all cells)\n", + ) + oparser.add_option( + "-g", "--gc", + type="int", default=0, + help="0: auto gc enabled; 1: auto gc disabled, manual collect after each file; 2: no gc") + oparser.add_option( + "-s", "--onesheet", + default="", + help="restrict output to this sheet (name or index)") + oparser.add_option( + "-u", "--unnumbered", + action="store_true", default=0, + help="omit line numbers or offsets in biff_dump") + oparser.add_option( + "-d", "--on-demand", + action="store_true", default=0, + help="load sheets on demand instead of all at once") + oparser.add_option( + "-t", "--suppress-timing", + action="store_true", default=0, + help="don't print timings (diffs are less messy)") + oparser.add_option( + "-r", "--ragged-rows", + action="store_true", default=0, + help="open_workbook(..., ragged_rows=True)") + options, args = oparser.parse_args(cmd_args) + if len(args) == 1 and args[0] in ("version", ): + pass + elif len(args) < 2: + oparser.error("Expected at least 2 args, found %d" % len(args)) + cmd = args[0] + xlrd_version = getattr(xlrd, "__VERSION__", "unknown; before 0.5") + if cmd == 'biff_dump': + xlrd.dump(args[1], unnumbered=options.unnumbered) + sys.exit(0) + if cmd == 'biff_count': + xlrd.count_records(args[1]) + sys.exit(0) + if cmd == 'version': + print("xlrd: %s, from %s" % (xlrd_version, xlrd.__file__)) + print("Python:", sys.version) + sys.exit(0) + if options.logfilename: + logfile = LogHandler(open(options.logfilename, 'w')) + else: + logfile = sys.stdout + mmap_opt = options.mmap + mmap_arg = xlrd.USE_MMAP + if mmap_opt in (1, 0): + mmap_arg = mmap_opt + elif mmap_opt != -1: + print('Unexpected value (%r) for mmap option -- assuming default' % mmap_opt) + fmt_opt = options.formatting | (cmd in ('xfc', )) + gc_mode = options.gc + if gc_mode: + gc.disable() + for pattern in args[1:]: + for fname in glob.glob(pattern): + print("\n=== File: %s ===" % fname) + if logfile != sys.stdout: + logfile.setfileheading("\n=== File: %s ===\n" % fname) + if gc_mode == 1: + n_unreachable = gc.collect() + if n_unreachable: + print("GC before open:", n_unreachable, "unreachable objects") + try: + t0 = time.time() + bk = xlrd.open_workbook( + fname, + verbosity=options.verbosity, logfile=logfile, + use_mmap=mmap_arg, + encoding_override=options.encoding, + formatting_info=fmt_opt, + on_demand=options.on_demand, + ragged_rows=options.ragged_rows, + ) + t1 = time.time() + if not options.suppress_timing: + print("Open took %.2f seconds" % (t1-t0,)) + except xlrd.XLRDError as e: + print("*** Open failed: %s: %s" % (type(e).__name__, e)) + continue + except KeyboardInterrupt: + print("*** KeyboardInterrupt ***") + traceback.print_exc(file=sys.stdout) + sys.exit(1) + except BaseException as e: + print("*** Open failed: %s: %s" % (type(e).__name__, e)) + traceback.print_exc(file=sys.stdout) + continue + t0 = time.time() + if cmd == 'hdr': + bk_header(bk) + elif cmd == 'ov': # OverView + show(bk, 0) + elif cmd == 'show': # all rows + show(bk) + elif cmd == '2rows': # first row and last row + show(bk, 2) + elif cmd == '3rows': # first row, 2nd row and last row + show(bk, 3) + elif cmd == 'bench': + show(bk, printit=0) + elif cmd == 'fonts': + bk_header(bk) + show_fonts(bk) + elif cmd == 'names': # named reference list + show_names(bk) + elif cmd == 'name_dump': # named reference list + show_names(bk, dump=1) + elif cmd == 'labels': + show_labels(bk) + elif cmd == 'xfc': + count_xfs(bk) + else: + print("*** Unknown command <%s>" % cmd) + sys.exit(1) + del bk + if gc_mode == 1: + n_unreachable = gc.collect() + if n_unreachable: + print("GC post cmd:", fname, "->", n_unreachable, "unreachable objects") + if not options.suppress_timing: + t1 = time.time() + print("\ncommand took %.2f seconds\n" % (t1-t0,)) + + return None + + av = sys.argv[1:] + if not av: + main(av) + firstarg = av[0].lower() + if firstarg == "hotshot": + import hotshot + import hotshot.stats + av = av[1:] + prof_log_name = "XXXX.prof" + prof = hotshot.Profile(prof_log_name) + # benchtime, result = prof.runcall(main, *av) + result = prof.runcall(main, *(av, )) + print("result", repr(result)) + prof.close() + stats = hotshot.stats.load(prof_log_name) + stats.strip_dirs() + stats.sort_stats('time', 'calls') + stats.print_stats(20) + elif firstarg == "profile": + import cProfile + av = av[1:] + cProfile.run('main(av)', 'YYYY.prof') + import pstats + p = pstats.Stats('YYYY.prof') + p.strip_dirs().sort_stats('cumulative').print_stats(30) + else: + main(av) diff --git a/Scripts/sqlformat.exe b/Scripts/sqlformat.exe new file mode 100644 index 0000000..422a97f Binary files /dev/null and b/Scripts/sqlformat.exe differ diff --git a/Scripts/tokenize-rt.exe b/Scripts/tokenize-rt.exe new file mode 100644 index 0000000..803dd4e Binary files /dev/null and b/Scripts/tokenize-rt.exe differ diff --git a/Scripts/xml2odf b/Scripts/xml2odf new file mode 100644 index 0000000..7abbb8a --- /dev/null +++ b/Scripts/xml2odf @@ -0,0 +1,241 @@ +#!D:\wamp\www\psurvey\psurvey\Scripts\python.exe +# -*- coding: utf-8 -*- +# Copyright (C) 2006 Søren Roug, European Environment Agency +# +# This is free software. You may redistribute it under the terms +# of the Apache license and the GNU General Public License Version +# 2 or at your option any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Contributor(s): +# +# + +# OpenDocument can be a complete office document in a single +# XML document. This script will take such a document and create +# a package +import io +import zipfile,time, sys, getopt +import xml.sax, xml.sax.saxutils +from odf import manifest + +class SplitWriter: + def __init__(self): + self.activefiles = [] + self._content = [] + self._meta = [] + self._styles = [] + self._settings = [] + + self.files = {'content': self._content, 'meta': self._meta, + 'styles':self._styles, 'settings': self._settings } + + def write(self, str): + for f in self.activefiles: + f.append(str) + + def activate(self, filename): + file = self.files[filename] + if file not in self.activefiles: + self.activefiles.append(file) + + def deactivate(self, filename): + file = self.files[filename] + if file in self.activefiles: + self.activefiles.remove(file) + +odmimetypes = { + 'application/vnd.oasis.opendocument.text': '.odt', + 'application/vnd.oasis.opendocument.text-template': '.ott', + 'application/vnd.oasis.opendocument.graphics': '.odg', + 'application/vnd.oasis.opendocument.graphics-template': '.otg', + 'application/vnd.oasis.opendocument.presentation': '.odp', + 'application/vnd.oasis.opendocument.presentation-template': '.otp', + 'application/vnd.oasis.opendocument.spreadsheet': '.ods', + 'application/vnd.oasis.opendocument.spreadsheet-template': '.ots', + 'application/vnd.oasis.opendocument.chart': '.odc', + 'application/vnd.oasis.opendocument.chart-template': '.otc', + 'application/vnd.oasis.opendocument.image': '.odi', + 'application/vnd.oasis.opendocument.image-template': '.oti', + 'application/vnd.oasis.opendocument.formula': '.odf', + 'application/vnd.oasis.opendocument.formula-template': '.otf', + 'application/vnd.oasis.opendocument.text-master': '.odm', + 'application/vnd.oasis.opendocument.text-web': '.oth', +} + +OFFICENS = u"urn:oasis:names:tc:opendocument:xmlns:office:1.0" +base = xml.sax.saxutils.XMLGenerator + +class odfsplitter(base): + + def __init__(self): + self._mimetype = '' + self.output = SplitWriter() + self._prefixes = [] + base.__init__(self, self.output, 'utf-8') + + def startPrefixMapping(self, prefix, uri): + base.startPrefixMapping(self, prefix, uri) + self._prefixes.append('xmlns:%s="%s"' % (prefix, uri)) + + def startElementNS(self, name, qname, attrs): + if name == (OFFICENS, u"document"): + self._mimetype = attrs.get((OFFICENS, "mimetype")) + elif name == (OFFICENS, u"meta"): + self.output.activate('meta') + + elif name == (OFFICENS, u"settings"): + self.output.activate('settings') + elif name == (OFFICENS, u"scripts"): + self.output.activate('content') + elif name == (OFFICENS, u"font-face-decls"): + self.output.activate('content') + self.output.activate('styles') + elif name == (OFFICENS, u"styles"): + self.output.activate('styles') + elif name == (OFFICENS, u"automatic-styles"): + self.output.activate('content') + self.output.activate('styles') + elif name == (OFFICENS, u"master-styles"): + self.output.activate('styles') + elif name == (OFFICENS, u"body"): + self.output.activate('content') + base.startElementNS(self, name, qname, attrs) + + def endElementNS(self, name, qname): + base.endElementNS(self, name, qname) + if name == (OFFICENS, u"meta"): + self.output.deactivate('meta') + elif name == (OFFICENS, u"settings"): + self.output.deactivate('settings') + elif name == (OFFICENS, u"scripts"): + self.output.deactivate('content') + elif name == (OFFICENS, u"font-face-decls"): + self.output.deactivate('content') + self.output.deactivate('styles') + elif name == (OFFICENS, u"styles"): + self.output.deactivate('styles') + elif name == (OFFICENS, u"automatic-styles"): + self.output.deactivate('content') + self.output.deactivate('styles') + elif name == (OFFICENS, u"master-styles"): + self.output.deactivate('styles') + elif name == (OFFICENS, u"body"): + self.output.deactivate('content') + + + def content(self): + """ Return the content inside a wrapper called + """ + prefixes = ' '.join(self._prefixes) + return ''.join(['\n' % prefixes] + list(map(lambda x: x.decode("utf-8"), self.output._content)) + ['']) + + def settings(self): + prefixes = ' '.join(self._prefixes).encode('utf-8') + return ''.join( ['\n' % prefixes] + self.output._settings + ['''''']) + + def styles(self): + prefixes = ' '.join(self._prefixes) + return ''.join( ['\n' % prefixes] + list(map(lambda x: x.decode("utf-8"), self.output._styles)) + ['''''']) + + def meta(self): + prefixes = ' '.join(self._prefixes) + return ''.join( ['\n' % prefixes] + list(map(lambda x: x.decode("utf-8"), self.output._meta)) + ['''''']) + +def usage(): + sys.stderr.write("Usage: %s [-o outputfile] [-s] inputfile\n" % sys.argv[0]) + +def manifestxml(m): + """ Generates the content of the manifest.xml file """ + xml=io.StringIO() + xml.write(u"\n") + m.toXml(0,xml) + return xml.getvalue() + +try: + opts, args = getopt.getopt(sys.argv[1:], "o:s", ["output=","suffix"]) +except getopt.GetoptError: + usage() + sys.exit(2) + +outputfile = '-' +addsuffix = False + +for o, a in opts: + if o in ("-o", "--output"): + outputfile = a + if o in ("-s", "--suffix"): + addsuffix = True + +if len(args) > 1: + usage() + sys.exit(2) + +odfs = odfsplitter() +parser = xml.sax.make_parser() +parser.setFeature(xml.sax.handler.feature_namespaces, 1) +parser.setContentHandler(odfs) +if len(args) == 0: + parser.parse(sys.stdin) +else: + parser.parse(open(args[0],"r")) + +mimetype = odfs._mimetype +suffix = odmimetypes.get(mimetype,'.xxx') + +if outputfile == '-': + if sys.stdout.isatty(): + sys.stderr.write("Won't write ODF file to terminal\n") + sys.exit(1) + z = zipfile.ZipFile(sys.stdout,"w") +else: + if addsuffix: + outputfile = outputfile + suffix + z = zipfile.ZipFile(outputfile,"w") + +now = time.localtime()[:6] + +# Write mimetype +zi = zipfile.ZipInfo('mimetype', now) +zi.compress_type = zipfile.ZIP_STORED +z.writestr(zi,mimetype) + +# Write content +zi = zipfile.ZipInfo("content.xml", now) +zi.compress_type = zipfile.ZIP_DEFLATED +z.writestr(zi,odfs.content() ) +# Write styles +zi = zipfile.ZipInfo("styles.xml", now) +zi.compress_type = zipfile.ZIP_DEFLATED +z.writestr(zi,odfs.styles() ) + +# Write meta +zi = zipfile.ZipInfo("meta.xml", now) +zi.compress_type = zipfile.ZIP_DEFLATED +z.writestr(zi,odfs.meta() ) + +m = manifest.Manifest() +m.addElement(manifest.FileEntry(fullpath="/", mediatype=mimetype)) +m.addElement(manifest.FileEntry(fullpath="content.xml",mediatype="text/xml")) +m.addElement(manifest.FileEntry(fullpath="styles.xml", mediatype="text/xml")) +m.addElement(manifest.FileEntry(fullpath="meta.xml", mediatype="text/xml")) + +# Write manifest +zi = zipfile.ZipInfo("META-INF/manifest.xml", now) +zi.compress_type = zipfile.ZIP_DEFLATED +z.writestr(zi, manifestxml(m).encode("utf-8") ) +z.close() + + + +# Local Variables: *** +# mode: python *** +# End: *** diff --git a/authApp/managers.py b/authApp/managers.py index 8ec3cd4..f559623 100755 --- a/authApp/managers.py +++ b/authApp/managers.py @@ -1,5 +1,5 @@ from django.contrib.auth.base_user import BaseUserManager -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ class CustomUserManager(BaseUserManager): @@ -7,6 +7,7 @@ class CustomUserManager(BaseUserManager): Custom user model manager where email is the unique identifiers for authentication instead of usernames. """ + def create_user(self, email, password, **extra_fields): """ Create and save a User with the given email and password. @@ -31,4 +32,4 @@ def create_superuser(self, email, password, **extra_fields): raise ValueError(_('Superuser must have is_staff=True.')) if extra_fields.get('is_superuser') is not True: raise ValueError(_('Superuser must have is_superuser=True.')) - return self.create_user(email, password, **extra_fields) \ No newline at end of file + return self.create_user(email, password, **extra_fields) diff --git a/authApp/urls.py b/authApp/urls.py index 5cf426d..6cd087c 100755 --- a/authApp/urls.py +++ b/authApp/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import url +# from django.urls import re_path from django.contrib.staticfiles.urls import staticfiles_urlpatterns from pSurvey import settings diff --git a/pSurvey/settings.py b/pSurvey/settings.py index f423543..dfc3d75 100755 --- a/pSurvey/settings.py +++ b/pSurvey/settings.py @@ -34,6 +34,7 @@ def get_secret(setting, secrets=secrets): # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ + # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'b6&xe78vjs@fr3hjek^36e#01nrq*tgipu5q49s9p0n2-f0q5)' @@ -56,6 +57,8 @@ def get_secret(setting, secrets=secrets): 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', + 'django.contrib.humanize', + 'import_export', 'django.contrib.staticfiles', 'rest_framework', 'rest_framework.authtoken', @@ -84,8 +87,7 @@ def get_secret(setting, secrets=secrets): TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(BASE_DIR, 'templates')] - , + 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ @@ -101,7 +103,6 @@ def get_secret(setting, secrets=secrets): WSGI_APPLICATION = 'pSurvey.wsgi.application' - # Database # https://docs.djangoproject.com/en/3.1/ref/settings/#databases @@ -111,7 +112,8 @@ def get_secret(setting, secrets=secrets): 'NAME': 'psurveyapi', 'USER': get_secret('USER'), 'PASSWORD': get_secret('PWD'), - 'HOST': get_secret('HOST'), # Or an IP Address that your DB is hosted on + # Or an IP Address that your DB is hosted on + 'HOST': get_secret('HOST'), 'PORT': get_secret('DB_PORT'), } } diff --git a/pSurvey/urls.py b/pSurvey/urls.py index 5556e6c..f30a67e 100755 --- a/pSurvey/urls.py +++ b/pSurvey/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import url +from django.urls import re_path from django.contrib import admin from django.conf.urls.static import serve from django.urls import path, include @@ -7,16 +7,16 @@ from . import settings urlpatterns = [ path('admin/', admin.site.urls), - url(r'^', include('authApp.urls')), - url(r'^', include('survey.urls')), - url(r'^', include('reports.urls')), - url(r'^auth/', include('djoser.urls')), - url(r'^auth/', include('djoser.urls.authtoken')), + re_path(r'^', include('authApp.urls')), + re_path(r'^', include('survey.urls')), + re_path(r'^', include('reports.urls')), + re_path(r'^auth/', include('djoser.urls')), + re_path(r'^auth/', include('djoser.urls.authtoken')), ] if settings.DEBUG: urlpatterns += [ - url(r'^static/(?P.*)$', serve, { + re_path(r'^static/(?P.*)$', serve, { 'document_root': settings.STATIC_ROOT }) ] diff --git a/pyvenv.cfg b/pyvenv.cfg index d9bf1c1..08aff85 100644 --- a/pyvenv.cfg +++ b/pyvenv.cfg @@ -1,3 +1,5 @@ -home = C:\Users\Kevin\AppData\Local\Programs\Python\Python310 +home = C:\Users\Kevin\AppData\Local\Programs\Python\Python311 include-system-site-packages = false -version = 3.10.7 +version = 3.11.6 +executable = C:\Users\Kevin\AppData\Local\Programs\Python\Python311\python.exe +command = C:\Users\Kevin\AppData\Local\Programs\Python\Python311\python.exe -m venv D:\wamp\www\psurvey\psurvey diff --git a/reports/urls.py b/reports/urls.py index 0026105..dd23a63 100755 --- a/reports/urls.py +++ b/reports/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import url +from django.urls import re_path from django.contrib.auth.decorators import login_required from django.contrib.staticfiles.urls import staticfiles_urlpatterns from rest_framework import routers @@ -20,7 +20,7 @@ path('web/reports/users', views.users_report, name='users_report'), path('web/reports/patients', views.patients_report, name='patients_report'), path('web/patients/list', views.Patients, name='patients_list'), - url('^api/', include(router.urls)), + re_path('^api/', include(router.urls)), ] urlpatterns += staticfiles_urlpatterns() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 0052bec..3cefdc4 100755 Binary files a/requirements.txt and b/requirements.txt differ diff --git a/survey/admin.py b/survey/admin.py index 8c38f3f..736fb00 100755 --- a/survey/admin.py +++ b/survey/admin.py @@ -1,3 +1,21 @@ from django.contrib import admin +from import_export import resources +from .models import Questionnaire_Data +import json # Register your models here. + +class QuestionnaireDataResource(resources.ModelResource): + + class Meta: + model = Questionnaire_Data + # fields = ('questionnaire_id', 'mfl_code', 'ccc_number',) + # exclude = ('id', ) + import_id_fields = ["questionnaire_id","mfl_code", "ccc_number"] + # exclude = ('id',) + skip_unchanged = True + use_bulk = True + + + +# admin.site.register(QuestionnaireDataResource) diff --git a/survey/forms.py b/survey/forms.py index 035d879..6efd4e6 100755 --- a/survey/forms.py +++ b/survey/forms.py @@ -18,3 +18,7 @@ class LoginForm(forms.Form): } ) ) + +class QuestionnaireDataForm(forms.Form): + ccc_number = forms.CharField(label="CCC Number",max_length=15) + mfl_code = forms.CharField(label="MFL Code",max_length=15) diff --git a/survey/models.py b/survey/models.py index cfdda71..2f26ba7 100755 --- a/survey/models.py +++ b/survey/models.py @@ -1,6 +1,7 @@ from datetime import datetime from django.db import models +from django.forms import ModelForm from authApp.models import Facility, Users @@ -78,6 +79,15 @@ class Meta: db_table = "Started_Questionnaire" +class Questionnaire_Data (models.Model): + questionnaire = models.ForeignKey(Questionnaire, on_delete=models.CASCADE) + ccc_number = models.CharField(max_length=15, null=True) + mfl_code = models.PositiveIntegerField() + has_completed_survey = models.BooleanField(default=False) + + class Meta: + db_table = "Questionnaire_data" + class Response (models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) answer = models.ForeignKey(Answer, on_delete=models.CASCADE, null=True) @@ -170,3 +180,9 @@ class Meta: db_table = "vw_questionnaire_responses_flat_table" unique_together = ('survey_id', 'ccc_no', 'other_non_verification_reason', 'date_of_last_vl', 'art_start_date', 'gender') + + +class QuestionnaireDataForm(ModelForm): + class Meta: + model = Questionnaire_Data + fields = ["ccc_number", "mfl_code"] \ No newline at end of file diff --git a/survey/serializer.py b/survey/serializer.py index 7346a60..e9006b8 100755 --- a/survey/serializer.py +++ b/survey/serializer.py @@ -9,6 +9,11 @@ class Meta: fields = '__all__' +class QuestionnaireDataSerializer(serializers.ModelSerializer): + class Meta: + model = Questionnaire_Data + fields = '__all__' + class QuestionSerializer(serializers.ModelSerializer): class Meta: diff --git a/survey/urls.py b/survey/urls.py index a739f60..6e91b51 100755 --- a/survey/urls.py +++ b/survey/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import url +# from django.urls import re_path from django.contrib.staticfiles.urls import staticfiles_urlpatterns from django.urls import path @@ -16,6 +16,10 @@ views.edit_questionnaire, name='edit-questionnaires'), path('web/manage-data//', views.manage_data, name='manage-data'), + path('web/edit-data/', + views.edit_data, name='edit-data'), + path('web/delete-data//', + views.delete_question, name='delete-data'), path('web/publish-questionnaire//', views.publish_questionnaire, name='publish-questionnaires'), path('web/add-question//', diff --git a/survey/views.py b/survey/views.py index e4eceef..eb06394 100755 --- a/survey/views.py +++ b/survey/views.py @@ -19,15 +19,21 @@ from django.shortcuts import render, redirect from docutils.nodes import status from rest_framework import status +from rest_framework import generics +import pandas as pd +from tablib import Dataset from rest_framework.response import Response as Res -from rest_framework.decorators import api_view, permission_classes +from rest_framework.renderers import JSONRenderer, TemplateHTMLRenderer +from rest_framework.decorators import api_view, permission_classes, renderer_classes from rest_framework.permissions import IsAuthenticated +from import_export import resources from survey.bulk_manager import BulkCreateManager from .models import * from .serializer import * +from .forms import QuestionnaireDataForm from authApp.serializer import * from string import digits @@ -319,7 +325,10 @@ def index(request): user = request.user is_active = request.POST.get('active') - if user.access_level.id == 2 or user.access_level.id == 3: + if user.access_level.id == 3: + fac_quest = Facility.objects.filter(id__in=Users.objects.filter( + id__in=Started_Questionnaire.objects.filter(id__in=End_Questionnaire.objects.all().values_list('session_id', flat=True)).values_list('started_by_id', flat=True)).values_list('facility_id', flat=True).distinct()).order_by('county', 'sub_county', 'name') + fac = Facility.objects.all().order_by('county', 'sub_county', 'name') quest = Questionnaire.objects.filter(is_active=True).order_by('name') if is_active == 'active': @@ -330,38 +339,39 @@ def index(request): is_active=False).values_list('id', flat=True) aq = Questionnaire.objects.filter( is_active=True, active_till__gte=date.today()) - # resp = End_Questionnaire.objects.filter() - resp = ResponsesFlat.objects.filter() + resp = End_Questionnaire.objects.filter() + # resp = ResponsesFlat.objects.filter() queryset = Facility.objects.all().distinct('county') org = Partner.objects.all().order_by('name') data1 = [] - unverified = Partner.objects.filter().values('name', 'unverified') - submitted = ResponsesFlat.objects.filter().annotate(name=F('partner_name') - ).values('name').annotate(c=Count('survey_id')).values('name', 'c').order_by('c') + # resp = [] + # unverified = Partner.objects.filter().values('name', 'unverified') + # submitted = ResponsesFlat.objects.filter().annotate(name=F('partner_name') ).values('name').annotate(c=Count('survey_id')).values('name', 'c').order_by('c') # print(submitted) - model_combination = list(chain(unverified, submitted)) - out = {} - for d in model_combination: - out[d["name"]] = {**out.get(d["name"], {}), **d} - - out = list(out.values()) - # get percentages - for o in out: - try: - o['perc'] = float("{0:.2f}".format( - (o['c']) * 100/o['unverified'])) - o['pending'] = o['unverified'] - o['c'] - data1.append(o) - except: - pass - # sort - data1 = sorted(data1, key=lambda d: d['perc'], reverse=True) + # model_combination = list(chain(unverified, submitted)) + # out = {} + # for d in model_combination: + # out[d["name"]] = {**out.get(d["name"], {}), **d} + + # out = list(out.values()) + # # get percentages + # for o in out: + # try: + # o['perc'] = float("{0:.2f}".format( + # (o['c']) * 100/o['unverified'])) + # o['pending'] = o['unverified'] - o['c'] + # data1.append(o) + # except: + # pass + # # sort + # data1 = sorted(data1, key=lambda d: d['perc'], reverse=True) context = { 'u': user, 'fac': fac, + 'fac_quest': fac_quest, 'quest': quest, 'aq': aq, 'resp': resp, @@ -1080,6 +1090,9 @@ def publish_questionnaire(request, q_id): @login_required def manage_data(request, q_id): + + data = [] + context = {} user = request.user u = user question = Questionnaire.objects.get(id=q_id) @@ -1087,11 +1100,63 @@ def manage_data(request, q_id): context = { 'u': u, 'q': question, + 'q_data': [], } - return render(request, 'survey/manage_data.html', context) + if request.method == "POST": + try: + file = request.FILES['excel_file'] + df = pd.read_excel(file) + + #Rename the headers in the excel file to match Django models fields + rename_columns = {"MFL Code": "mfl_code", "CCC No": "ccc_number"} + df.rename(columns = rename_columns, inplace=True) + + # add the questionnaire id + # return HttpResponse(json.dumps(len(df))) + row_count = len(df) if len(df) > 0 else 0 + # df["id"] = [8] * row_count + df["questionnaire"] = [q_id] * row_count + + # add key field if any + id_values = [] + for row in df.itertuples(): + if Questionnaire_Data.objects.filter(questionnaire=row.questionnaire,mfl_code = row.mfl_code, ccc_number =row.ccc_number).exists(): + id_val = Questionnaire_Data.objects.get(questionnaire=row.questionnaire,mfl_code = row.mfl_code, ccc_number =row.ccc_number).id + id_values.append(str(id_val)) + else: + id_values.append(None) + + df["id"] = id_values + + #Call the questionnaire data Resource Model and make its instance + questionnaire_data_resource = resources.modelresource_factory(model=Questionnaire_Data)() + + # Load the pandas dataframe into a tablib dataset + dataset = Dataset().load(df) + + # Call the import_data hook and pass the tablib dataset + result = questionnaire_data_resource.import_data(dataset, dry_run=True, raise_errors = True) + + if not result.has_errors(): + result = questionnaire_data_resource.import_data(dataset, dry_run=False) + context['status'] = "Questionnaire Data Imported Successfully" + else: + context['status'] = "Questionnaire Data Not Imported " + except Exception as e: + raise Http404("Unable To Upload File. "+repr(e)) + + + + # get the questionnaire data + q_data = Questionnaire_Data.objects.filter(questionnaire=q_id).order_by('mfl_code','ccc_number') + serializer = QuestionnaireDataSerializer(q_data, many=True) + context['q_data'] = serializer.data + + return render(request, 'survey/manage_data.html', context) + @login_required def edit_questionnaire(request, q_id): user = request.user @@ -1495,6 +1560,34 @@ def add_question(request, q_id): } return render(request, 'survey/new_questions.html', context) +@login_required +@api_view(('POST',)) +def edit_data(request): + user = request.user + + if request.method == 'POST': + try: + q = Questionnaire_Data.objects.get(id=request.POST.get('q_id')) + ccc_number = request.POST.get('ccc_number') + mfl_code = request.POST.get('mfl_code') + + q.ccc_number = ccc_number + q.mfl_code = mfl_code + q.save() + + return Res({ + "success": True, + "Message": "Questionnaire data saved successfully👌!" + }, status.HTTP_200_OK) + except Exception as e: + return Res({ + "success": False, + "Message": repr(e) + }, status.HTTP_500_INTERNAL_SERVER_ERROR) + + + + @login_required def edit_question(request, q_id): diff --git a/templates/reports/questionnaire_report.html b/templates/reports/questionnaire_report.html index eadde2e..f9f007c 100644 --- a/templates/reports/questionnaire_report.html +++ b/templates/reports/questionnaire_report.html @@ -122,7 +122,7 @@

Survey Responses

{ extend: 'excel', filename: filename, - text: ' Excell Export ' , + text: ' Excel Export ' , exportOptions: { modifier: { search: 'applied', diff --git a/templates/survey/dashboard.html b/templates/survey/dashboard.html index e8640cb..f434fb3 100755 --- a/templates/survey/dashboard.html +++ b/templates/survey/dashboard.html @@ -1,5 +1,6 @@ {% extends 'survey/base.html' %} {% load static %} +{% load humanize %} {% block title %} Dashboard {% endblock %} @@ -33,24 +34,17 @@
Dashboard
- {% if u.access_level.id != 4 %} -
-
Number of facilities
- {{ fac.count }} -
- {% else %} -
-
Number of patients served
- {{ fac }} -
- {% endif %} +
+
Completed surveys
+ {{ resp.count|intcomma }} +
-
- +
+
-
+
@@ -60,7 +54,7 @@
Number of patients served<
Total Questionnaires
- {{ quest.count }} + {{ quest.count|intcomma }}
@@ -78,7 +72,7 @@
Total Questionnaires
Active Questionnaires
- {{ aq.count }} + {{ aq.count|intcomma }}
@@ -94,17 +88,26 @@
Active Questionnaires
-
-
Total completed surveys
- {{ resp.count }} -
+ {% if u.access_level.id != 4 %} +
+
Participating Facilities
+ {{ fac_quest.count|intcomma }} + of {{ fac.count|intcomma }} +
+ {% else %} +
+
Number of patients surveyed
+ {{ fac|intcomma }} +
+ {% endif %}
-
- +
+
+
-
- - -
-
-
-
-
-
-
-
-
-
Performance
-
Surveys' Performance
-
-
-
-
- -
- -
-
-
-
- {% if u.access_level.id == 2 or u.access_level.id == 3%} -
-
-
-
-
-
Partners' Performance - Proportion of surveys received against unverified numbers
-
-
-
-
- -
- -
-
-
-
-
- - - - - - - - - - - {% for foo in data %} - - - - - - - {% endfor %} - -
Partner#UnverifiedSurveys received% Surveys Proportion
{{ foo.name }}{{ foo.unverified }}{{ foo.c }}{{ foo.perc }}
-
- {% endif %} -
+ {% endblock %} {% block scripts %} diff --git a/templates/survey/manage_data.html b/templates/survey/manage_data.html index 46e33b0..2e6e0bb 100644 --- a/templates/survey/manage_data.html +++ b/templates/survey/manage_data.html @@ -41,7 +41,7 @@
Upload data
- +
@@ -52,9 +52,195 @@
Upload data
+ + +
 
+
+ + + + + + + + + + {% for foo in q_data %} + + + + + + {% endfor %} + + +
MFL CodeCCC No
{{ foo.mfl_code }}{{ foo.ccc_number }} + +
+
+
+ + + + + + + + + + + + +{% endblock %} + +{% block scripts %} + {% endblock %} \ No newline at end of file