diff --git a/client/getFromFirebase.js b/client/getFromFirebase.js new file mode 100644 index 0000000..12f4a45 --- /dev/null +++ b/client/getFromFirebase.js @@ -0,0 +1,300 @@ +// +// Get info from frebase and show it on the datatable +// + + + +var database = firebase.database(); +var arrayStocksHistory = []; + +var resultadoClone; + +var lastUpdate; + +var comparadores = { + patrLiq: { value: 2000000000, checked: 1 }, + liqCorr: { value: 1.5, checked: 1 }, + roe: { value: 20, checked: 1 }, + divPat: { value: 50, checked: 1 }, + cresc: { value: 5, checked: 1 }, + pvp: { value: 1.5, checked: 1 }, + pl: { value: 15, checked: 1 }, + dy: { value: 2.5, checked: 1 }, + plxpvp: { value: 22.5, checked: 1 }, +} + +initInputs(); + + +// ******************************* +// Logic functions +// ******************************* +database.ref().child('stocks').once('value').then(function(snapshot){ + // console.log(snapshot.val()) + + Object.keys(snapshot.val()).forEach(function(key){ + // console.log(snapshot.val()[key]) + var object = JSON.parse(snapshot.val()[key]); + arrayStocksHistory.push(object) + // console.log(object); + }) + + // Sort by date, the first one will be the newest + arrayStocksHistory.sort(function(a,b){ + var date1 = new Date(a.date); + var date2 = new Date(b.date); + return date2 - date1; + }) + + // console.log(arrayStocksHistory); + buildTable(arrayStocksHistory[0]); + hideLoading(); + +}, function(error){ + console.log("deu erro"); +}) + +// Get last update +database.ref().child('last_date').once('value').then(function(snapshot){ + + lastUpdate = Object.keys(snapshot.val()) + .map(function(key){ + return new Date(snapshot.val()[key]); + }) + .sort(function(a, b) { + console.log(new Date(a), new Date(b), new Date(a) > new Date(b)); + return new Date(b) > new Date(a) ? 1 : -1; + })[0]; + + $("#lastUpdate").append(lastUpdate); + +}, function(error){ + console.log("deu erro"); +}) + + + +var buildTable = function(data){ + // console.log('vamos montar a tabela', data); + + // Put the objects in an array + arrayObjects = []; + Object.keys(data).forEach(function(key){ + arrayObjects.push(data[key]) + }); + + arrayObjects = calculateScores(arrayObjects); + + var resultArray = []; + arrayObjects + .forEach(function(element) { + var innerObject = {}; + + if(Object.keys(element).pop().length > 3) { + Object.keys(element).map(function(innerKey) { + innerObject = element[innerKey]; + innerObject['papel'] = innerKey; + }); + + var myArray = []; + + myArray.push(innerObject["papel"]); + myArray.push(innerObject["nota"]); + myArray.push(innerObject["cotacao"]); + myArray.push(innerObject["Pat.Liq"]); + myArray.push(innerObject["Liq.Corr."]); + myArray.push(innerObject["ROE"]); + myArray.push(innerObject["Div.Brut/Pat."]); + myArray.push(innerObject["Cresc.5a"]); + myArray.push(innerObject["P/VP"]); + myArray.push(innerObject["P/L"]); + myArray.push(innerObject["DY"]); + myArray.push(innerObject["PSR"]); + myArray.push(innerObject["P/Ativo"]); + myArray.push(innerObject["P/Cap.Giro"]); + myArray.push(innerObject["P/EBIT"]); + myArray.push(innerObject["P/Ativ.Circ.Liq."]); + myArray.push(innerObject["EV/EBIT"]); + myArray.push(innerObject["EBITDA"]); + myArray.push(innerObject["Mrg.Liq."]); + myArray.push(innerObject["ROIC"]); + myArray.push(innerObject["Liq.2m."]); + resultArray.push(myArray); + + } + + }); + $('#resultado').DataTable({ + 'data': resultArray, + 'order' : [[ 1, "desc" ]], // Order by score + "pageLength": 500, // All stocks in the page + "createdRow": function( row, data, dataIndex ) { + if(data[1] >= 7.5) { + $(row).css('background-color', '#cde0cd') + } + } + }); + + // var txt = ""; + // for(var i = 0; i < arrayObjects.length; i++) { + // if(typeof(arrayObjects[i]) == "object"){ + + // Object.keys(arrayObjects[i]).forEach(function(stock){ + + // var greenLine = " class='green-line'"; + // // If we see a stock with score below 7.5 we turnoff green + // if(parseFloat(arrayObjects[i][stock]["nota"]) < 7.5) + // greenLine = ''; + + // var stockLine = "" + + // "" + stock + "" + + // "" + arrayObjects[i][stock]["nota"] + "" + + // "" + arrayObjects[i][stock]["cotacao"] + "" + + // "" + arrayObjects[i][stock]["Pat.Liq"] + "" + + // "" + arrayObjects[i][stock]["Liq.Corr."] + "" + + // "" + arrayObjects[i][stock]["ROE"] + "" + + // "" + arrayObjects[i][stock]["Div.Brut/Pat."] + "" + + // "" + arrayObjects[i][stock]["Cresc.5a"] + "" + + // "" + arrayObjects[i][stock]["P/VP"] + "" + + // "" + arrayObjects[i][stock]["P/L"] + "" + + // "" + arrayObjects[i][stock]["DY"] + "" + + // "" + arrayObjects[i][stock]["PSR"] + "" + + // "" + arrayObjects[i][stock]["P/Ativo"] + "" + + // "" + arrayObjects[i][stock]["P/Cap.Giro"] + "" + + // "" + arrayObjects[i][stock]["P/EBIT"] + "" + + // "" + arrayObjects[i][stock]["P/Ativ.Circ.Liq."] + "" + + // "" + arrayObjects[i][stock]["EV/EBIT"] + "" + + // "" + arrayObjects[i][stock]["EBITDA"] + "" + + // "" + arrayObjects[i][stock]["Mrg.Liq."] + "" + + // "" + arrayObjects[i][stock]["ROIC"] + "" + + // "" + arrayObjects[i][stock]["Liq.2m."] + "" + + // ""; + + // txt += stockLine; + // }) + // } + // } + // $(function(){ + // $(".table-body").append(txt); + // console.log('estou aqui'); + // // $("#resultado").append('dsajdhayasyusadd'); + // }) +} + +var calculateScores = function(stockArray){ + console.log('calculando socres'); + let dividePor = 0; + Object.keys(comparadores).forEach(function(elem){ + if(comparadores[elem].checked) dividePor += 1; + }) + console.log('dividePor', dividePor); + + for(var i = 0; i < stockArray.length; i++) { + if(typeof(stockArray[i]) == "object"){ + Object.keys(stockArray[i]).forEach(function(stock){ + var nota = 0.0; + + var patrLiq = parseFloat(stockArray[i][stock]["Pat.Liq"].replace(/\./g, '').replace(/\,/g, '.')); + if(comparadores.patrLiq.checked && patrLiq > parseFloat(comparadores.patrLiq.value)) + nota = nota + 1 + var liqCorr = parseFloat(stockArray[i][stock]["Liq.Corr."].replace(/\./g, '').replace(/,/g, '.')); + if(comparadores.liqCorr.checked && liqCorr > parseFloat(comparadores.liqCorr.value)) + nota = nota + 1 + var roe = parseFloat(stockArray[i][stock]["ROE"].replace(/\./g, '').replace(/\,/g, '.').replace(/%/g, '')); + if(comparadores.roe.checked && roe > parseFloat(comparadores.roe.value)) + nota = nota + 1 + var divPat = parseFloat(stockArray[i][stock]["Div.Brut/Pat."].replace(/\./g, '').replace(/\,/g, '.').replace(/%/g, '')); + if(comparadores.divPat.checked && divPat * 100 < parseFloat(comparadores.divPat.value) && divPat > 0) + nota = nota + 1 + var cresc = parseFloat(stockArray[i][stock]["Cresc.5a"].replace(/\./g, '').replace(/\,/g, '.').replace(/%/g, '')); + if(comparadores.cresc.checked && cresc > parseFloat(comparadores.cresc.value)) + nota = nota + 1 + var pvp = parseFloat(stockArray[i][stock]["P/VP"].replace(/\./g, '').replace(/\,/g, '.').replace(/%/g, '')); + if(comparadores.pvp.checked && pvp < parseFloat(comparadores.pvp.value) && pvp > 0) + nota = nota + 1 + var pl = parseFloat(stockArray[i][stock]["P/L"].replace(/\./g, '').replace(/\,/g, '.').replace(/%/g, '')); + if(comparadores.pl.checked && pl < parseFloat(comparadores.pl.value) && pl > 0) + nota = nota + 1 + var dy = parseFloat(stockArray[i][stock]["DY"].replace(/\./g, '').replace(/\,/g, '.').replace(/%/g, '')); + if(comparadores.dy.checked && dy > parseFloat(comparadores.dy.value)) + nota = nota + 1 + if(comparadores.plxpvp.checked && pl * pvp < parseFloat(comparadores.plxpvp.value)) + nota = nota + 1; + + stockArray[i][stock]["nota"] = (parseFloat(nota) / dividePor * 10.0).toFixed(2); + + // console.log([stock, stockArray[i][stock]["nota"], patrLiq, liqCorr, roe, divPat, cresc, pvp, pl, dy]) + + }); + } + } + + return stockArray; +} + + + +// Clears the table +var resetTable = function() { + if ( $.fn.DataTable.isDataTable('#resultado') ) { + $('#resultado').DataTable().destroy(); + } + console.log("reseta tabela", $(".table-body").html()) +} + + +// ******************************* +// View state functions +// ******************************* +var hideLoading = function(){ + $('#loading-whell').addClass("hidden"); + $('#info').removeClass("hidden"); +} + + +function initInputs(){ + console.log("chamei"); + $(function() { + $('#patrLiqInput').val(comparadores.patrLiq.value); + $('#liqCorrInput').val(comparadores.liqCorr.value); + $('#divPatInput').val(comparadores.divPat.value); + $('#dyInput').val(comparadores.dy.value); + $('#crescInput').val(comparadores.cresc.value); + $('#roeInput').val(comparadores.roe.value); + $('#pvpInput').val(comparadores.pvp.value); + $('#plInput').val(comparadores.pl.value); + $('#plxpvpInput').val(comparadores.plxpvp.value); + + resultadoClone = $(".table-body").clone(); + }) +} + +// Save rules +$(document).on('click', '#saveRules', function(){ + + // Get if checkbox is checked + comparadores.patrLiq.checked = $('#patrLiqCheckbox:checked').length; + comparadores.liqCorr.checked = $('#liqCorrCheckbox:checked').length; + comparadores.roe.checked = $('#roeCheckbox:checked').length; + comparadores.divPat.checked = $('#divPatCheckbox:checked').length; + comparadores.cresc.checked = $('#crescCheckbox:checked').length; + comparadores.pvp.checked = $('#pvpCheckbox:checked').length; + comparadores.pl.checked = $('#plCheckbox:checked').length; + comparadores.dy.checked = $('#dyCheckbox:checked').length; + comparadores.plxpvp.checked = $('#plxpvpCheckbox:checked').length; + + // get new values + comparadores.patrLiq.value = $('#patrLiqInput').val(); + comparadores.liqCorr.value = $('#liqCorrInput').val(); + comparadores.roe.value = $('#roeInput').val(); + comparadores.divPat.value = $('#divPatInput').val(); + comparadores.cresc.value = $('#crescInput').val(); + comparadores.pvp.value = $('#pvpInput').val(); + comparadores.pl.value = $('#plInput').val(); + comparadores.dy.value = $('#dyInput').val(); + comparadores.plxpvp.value = $('#plxpvpInput').val(); + + resetTable(); + buildTable(arrayStocksHistory[0]); + +}) \ No newline at end of file diff --git a/client/styles.css b/client/styles.css new file mode 100644 index 0000000..1ba218f --- /dev/null +++ b/client/styles.css @@ -0,0 +1,61 @@ +@keyframes App-logo-spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +th { + background: white; + position: sticky; + top: 0; + z-index: 10; + background: #dddddd; + cursor: pointer; +} + +.hidden{display:none;} + +.screen { + position: relative; + width: 100%; + height: 200px; +} + + +.loading-wrapper { + position: relative; +} + +.loading-wrapper .loading-screen { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 10; + background-color: rgba(250,250,250,0.7); +} + +.loading-wheel { + z-index: 11; + position: absolute; + top: 50%; + left: 50%; + margin-top: -50px; + margin-left: -50px; + border: 8px solid #e3e3e3; + border-radius: 50%; + border-top: 8px solid #4e8df9; + width: 100px; + height: 100px; + transform: translateY(-50%); + -webkit-animation: App-logo-spin 1.5s linear infinite; + animation: App-logo-spin 1.5s linear infinite; +} + +.stock { + color: #0062ff; +} + +.green-line { + background-color: #cde0cd; +} \ No newline at end of file diff --git a/functions/.gitignore b/functions/.gitignore new file mode 100644 index 0000000..7fbb8b4 --- /dev/null +++ b/functions/.gitignore @@ -0,0 +1,8 @@ +## Compiled JavaScript files +**/*.js +**/*.js.map + +# Typescript v1 declaration files +typings/ + +node_modules/ \ No newline at end of file diff --git a/functions/package.json b/functions/package.json new file mode 100644 index 0000000..d9e8a8f --- /dev/null +++ b/functions/package.json @@ -0,0 +1,27 @@ +{ + "name": "functions", + "scripts": { + "lint": "tslint --project tsconfig.json", + "build": "tsc", + "serve": "npm run build && firebase serve --only functions", + "shell": "npm run build && firebase functions:shell", + "start": "npm run shell", + "deploy": "firebase deploy --only functions", + "logs": "firebase functions:log" + }, + "engines": { + "node": "8" + }, + "main": "lib/index.js", + "dependencies": { + "firebase-admin": "^8.0.0", + "firebase-functions": "^3.0.0", + "mysql": "^2.17.1" + }, + "devDependencies": { + "@types/mysql": "2.15.6", + "tslint": "^5.12.0", + "typescript": "^3.2.2" + }, + "private": true +} diff --git a/functions/src/index.ts b/functions/src/index.ts new file mode 100644 index 0000000..e41e8bb --- /dev/null +++ b/functions/src/index.ts @@ -0,0 +1,132 @@ +import { Change } from 'firebase-functions'; +import { DataSnapshot } from 'firebase-functions/lib/providers/database'; +import * as Mysql from 'mysql'; + +// // Start writing Firebase Functions +// // https://firebase.google.com/docs/functions/typescript +// +// export const helloWorld = functions.https.onRequest((request, response) => { +// response.send("Hello from Firebase!"); +// }); + +export const saveStockHistory = (change: Change, context = null) => { + // export const saveStockHistory = functions.database.ref('/stocks') + // .onWrite((change: Change, context) => { + + const arrayStocksHistory = []; + + const val = change.after.val; + + + Object.keys(val).forEach((key) => { + // console.log(snapshot.val()[key]) + var object = JSON.parse(val[key]); + arrayStocksHistory.push(object) + // console.log(object); + }) + + // Sort by date, the first one will be the newest + arrayStocksHistory.sort((a,b) => { + var date1: any = new Date(a.date); + var date2 : any= new Date(b.date); + return date2 - date1; + }) + + + + const conn: Mysql.Connection = Mysql.createConnection({ + host: process.env.DB_ENDPOINT, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DATABASE_NAME, + }); + + + query(conn, "show tables", {}).then((res) => { + console.log(res); + + conn.destroy(); + }) + .catch(err => conn.destroy()); + + + // Grab the current value of what was written to the Realtime Database. + console.log('Payload:', change, "Context", context); + return ['Uppercasing', context, change]; + // const uppercase = original.toUpperCase(); + // You must return a Promise when performing asynchronous tasks inside a Functions such as + // writing to the Firebase Realtime Database. + // Setting an "uppercase" sibling in the Realtime Database returns a Promise. + // return snapshot.ref.parent.child('uppercase').set(uppercase); + // }); +}; + + +async function query(conn: Mysql.Connection, queryString: string, substituionValues: Object): Promise { + const queryOptions: Mysql.QueryOptions = transformIntoMySQLString(queryString, substituionValues); + + return new Promise((resolve, reject) => { + conn.query( + queryOptions, + (err: Mysql.MysqlError | null, results?: any, fields?: Mysql.FieldInfo[]) => { + if (err) reject(err); + else { + console.info('Salvo objeto com sucesso no DB'); + resolve(results); + } + } + ); + }); +} + +function transformIntoMySQLString(queryString: string, substituionValues: any): Mysql.QueryOptions { + + // Get all keys (they have a ':' in front of the name) + const allKeys: string[] = queryString.split(' ') + .filter((word: string) => word.indexOf(':') == 0); + + const allValuesInOrder: Array = allKeys + .map((key: string) => substituionValues[key]); + + // In case of error + if (allKeys.length != allValuesInOrder.length) + throw ( + { 'queryString': queryString, subsValues: substituionValues } + ); + + + // Overwrite the keys with '?' + let MysqlQueryString: string = queryString; + for (const key of allKeys) { + MysqlQueryString = MysqlQueryString.replace(key, '?'); + } + + return { + sql: MysqlQueryString, + values: allValuesInOrder + }; +} + + +const change: any = null; +// { +// before: +// DataSnapshot: { +// app: +// FirebaseApp: { +// firebaseInternals_: [], +// services_: { }, +// isDeleted_: false, +// name_: '__admin__', +// options_: [], +// INTERNAL: [] }, +// instance: 'https://bovespastockratings.firebaseio.com', +// _path: '/stocks', +// _data: +// { +// '-LiAHa0uatNefaIDZ250': ' {\n "0": {\n "GBIO33": {\n "Cresc.5a": "0,00%",\n "DY": "0,00%",\n "Div.Brut/Pat.": "0,30",\n "EBITDA": "23,44%",\n "EV/EBIT": "0,00",\n "Liq.2m.": "1.360.630,00",\n "Liq.Corr.": "2,03",\n "Mrg.Liq.": "7,73%",\n "P/Ativ.Circ.Liq.": "0,00",\n "P/Ativo": "0,000",\n "P/Cap.Giro": "0,00",\n "P/EBIT": "0,00",\n "P/L": "0,00",\n "P/VP": "0,00",\n "PSR": "0,000",\n "Pat.Liq": "729.236.000,00",\n "ROE": "8,70%",\n "ROIC": "19,69%",\n "cotacao": "7,25"\n }\n },\n "1": {\n "STBP3": {\n "Cresc.5a": "-2,25%",\n "DY": "0,10%",\n "Div.Brut/Pat.": "0,16",\n "EBITDA": "5,32%",\n "EV/EBIT": "58,74",\n "Liq.2m.": "6.607.860,00",\n "Liq.Corr.": "1,23",\n "Mrg.Liq.": "-0,01%",\n "P/Ativ.Circ.Liq.": "-2,58",\n "P/Ativo": "1,024",\n "P/Cap.Giro": "39,43",\n "P/EBIT": "59,37",\n "P/L": "-26.388,80",\n "P/VP": "2,21",\n "PSR": "3,161",\n "Pat.Liq": "1.337.190.000,00",\n "ROE": "-0,01%",\n "ROIC": "1,93%",\n "cotacao": "4,43"\n }\n }' +// } +// } +// } + +saveStockHistory(change); \ No newline at end of file diff --git a/functions/tsconfig.json b/functions/tsconfig.json new file mode 100644 index 0000000..7ce05d0 --- /dev/null +++ b/functions/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "module": "commonjs", + "noImplicitReturns": true, + "noUnusedLocals": true, + "outDir": "lib", + "sourceMap": true, + "strict": true, + "target": "es2017" + }, + "compileOnSave": true, + "include": [ + "src" + ] +} diff --git a/functions/tslint.json b/functions/tslint.json new file mode 100644 index 0000000..98b2bfd --- /dev/null +++ b/functions/tslint.json @@ -0,0 +1,115 @@ +{ + "rules": { + // -- Strict errors -- + // These lint rules are likely always a good idea. + + // Force function overloads to be declared together. This ensures readers understand APIs. + "adjacent-overload-signatures": true, + + // Do not allow the subtle/obscure comma operator. + "ban-comma-operator": true, + + // Do not allow internal modules or namespaces . These are deprecated in favor of ES6 modules. + "no-namespace": true, + + // Do not allow parameters to be reassigned. To avoid bugs, developers should instead assign new values to new vars. + "no-parameter-reassignment": true, + + // Force the use of ES6-style imports instead of /// imports. + "no-reference": true, + + // Do not allow type assertions that do nothing. This is a big warning that the developer may not understand the + // code currently being edited (they may be incorrectly handling a different type case that does not exist). + "no-unnecessary-type-assertion": true, + + // Disallow nonsensical label usage. + "label-position": true, + + // Disallows the (often typo) syntax if (var1 = var2). Replace with if (var2) { var1 = var2 }. + "no-conditional-assignment": true, + + // Disallows constructors for primitive types (e.g. new Number('123'), though Number('123') is still allowed). + "no-construct": true, + + // Do not allow super() to be called twice in a constructor. + "no-duplicate-super": true, + + // Do not allow the same case to appear more than once in a switch block. + "no-duplicate-switch-case": true, + + // Do not allow a variable to be declared more than once in the same block. Consider function parameters in this + // rule. + "no-duplicate-variable": [true, "check-parameters"], + + // Disallows a variable definition in an inner scope from shadowing a variable in an outer scope. Developers should + // instead use a separate variable name. + "no-shadowed-variable": true, + + // Empty blocks are almost never needed. Allow the one general exception: empty catch blocks. + "no-empty": [true, "allow-empty-catch"], + + // Functions must either be handled directly (e.g. with a catch() handler) or returned to another function. + // This is a major source of errors in Cloud Functions and the team strongly recommends leaving this rule on. + "no-floating-promises": true, + + // Do not allow any imports for modules that are not in package.json. These will almost certainly fail when + // deployed. + "no-implicit-dependencies": true, + + // The 'this' keyword can only be used inside of classes. + "no-invalid-this": true, + + // Do not allow strings to be thrown because they will not include stack traces. Throw Errors instead. + "no-string-throw": true, + + // Disallow control flow statements, such as return, continue, break, and throw in finally blocks. + "no-unsafe-finally": true, + + // Expressions must always return a value. Avoids common errors like const myValue = functionReturningVoid(); + "no-void-expression": [true, "ignore-arrow-function-shorthand"], + + // Disallow duplicate imports in the same file. + "no-duplicate-imports": true, + + + // -- Strong Warnings -- + // These rules should almost never be needed, but may be included due to legacy code. + // They are left as a warning to avoid frustration with blocked deploys when the developer + // understand the warning and wants to deploy anyway. + + // Warn when an empty interface is defined. These are generally not useful. + "no-empty-interface": {"severity": "warning"}, + + // Warn when an import will have side effects. + "no-import-side-effect": {"severity": "warning"}, + + // Warn when variables are defined with var. Var has subtle meaning that can lead to bugs. Strongly prefer const for + // most values and let for values that will change. + "no-var-keyword": {"severity": "warning"}, + + // Prefer === and !== over == and !=. The latter operators support overloads that are often accidental. + "triple-equals": {"severity": "warning"}, + + // Warn when using deprecated APIs. + "deprecation": {"severity": "warning"}, + + // -- Light Warnings -- + // These rules are intended to help developers use better style. Simpler code has fewer bugs. These would be "info" + // if TSLint supported such a level. + + // prefer for( ... of ... ) to an index loop when the index is only used to fetch an object from an array. + // (Even better: check out utils like .map if transforming an array!) + "prefer-for-of": {"severity": "warning"}, + + // Warns if function overloads could be unified into a single function with optional or rest parameters. + "unified-signatures": {"severity": "warning"}, + + // Prefer const for values that will not change. This better documents code. + "prefer-const": {"severity": "warning"}, + + // Multi-line object literals and function calls should have a trailing comma. This helps avoid merge conflicts. + "trailing-comma": {"severity": "warning"} + }, + + "defaultSeverity": "error" +} diff --git a/gettingstarted/static/humans.txt b/gettingstarted/static/humans.txt new file mode 100644 index 0000000..e69de29 diff --git a/gettingstarted/urls.py b/gettingstarted/urls.py new file mode 100644 index 0000000..36492ed --- /dev/null +++ b/gettingstarted/urls.py @@ -0,0 +1,17 @@ +from django.conf.urls import include, url +from django.urls import path + +from django.contrib import admin +admin.autodiscover() + +import hello.views + +# Examples: +# url(r'^$', 'gettingstarted.views.home', name='home'), +# url(r'^blog/', include('blog.urls')), + +urlpatterns = [ + url(r'^$', hello.views.index, name='index'), + url(r'^db', hello.views.db, name='db'), + path('admin/', admin.site.urls), +] diff --git a/gettingstarted/wsgi.py b/gettingstarted/wsgi.py new file mode 100644 index 0000000..feb0af4 --- /dev/null +++ b/gettingstarted/wsgi.py @@ -0,0 +1,15 @@ +""" +WSGI config for gettingstarted project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/ +""" + +import os +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gettingstarted.settings") + +from django.core.wsgi import get_wsgi_application + +application = get_wsgi_application() diff --git a/hello/__init__.py b/hello/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hello/admin.py b/hello/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/hello/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/hello/migrations/0001_initial.py b/hello/migrations/0001_initial.py new file mode 100644 index 0000000..99c4a82 --- /dev/null +++ b/hello/migrations/0001_initial.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.1 on 2016-01-27 21:54 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Greeting', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('when', models.DateTimeField(auto_now_add=True, verbose_name=b'date created')), + ], + ), + ] diff --git a/hello/migrations/__init__.py b/hello/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hello/models.py b/hello/models.py new file mode 100644 index 0000000..89d76fd --- /dev/null +++ b/hello/models.py @@ -0,0 +1,5 @@ +from django.db import models + +# Create your models here. +class Greeting(models.Model): + when = models.DateTimeField('date created', auto_now_add=True) diff --git a/hello/static/lang-logo.png b/hello/static/lang-logo.png new file mode 100644 index 0000000..f04aff1 Binary files /dev/null and b/hello/static/lang-logo.png differ diff --git a/hello/templates/base.html b/hello/templates/base.html new file mode 100644 index 0000000..97ec073 --- /dev/null +++ b/hello/templates/base.html @@ -0,0 +1,83 @@ + + + +Python Getting Started on Heroku + + + + + + + + +{% block content %}{% endblock %} + + + diff --git a/hello/templates/db.html b/hello/templates/db.html new file mode 100644 index 0000000..e0e03d4 --- /dev/null +++ b/hello/templates/db.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} +{% load staticfiles %} + +{% block content %} +
+ + +

Page View Report

+ + +
    + +{% for greeting in greetings %} +
  • {{ greeting.when }}
  • +{% endfor %} + +
+ +
+ +{% endblock %} \ No newline at end of file diff --git a/hello/templates/index.html b/hello/templates/index.html new file mode 100644 index 0000000..8af2902 --- /dev/null +++ b/hello/templates/index.html @@ -0,0 +1,58 @@ +{% extends "base.html" %} +{% load staticfiles %} + +{% block content %} + +
+
+ +

Getting Started with Python on Heroku

+

This is a sample Python application deployed to Heroku. It's a reasonably simple app - but a good foundation for understanding how to get the most out of the Heroku platform.

+ Getting Started with Python + Source on GitHub +
+
+
+ +
+
+
+

How this sample app works

+
    +
  • This app was deployed to Heroku, either using Git or by using Heroku Button on the repository.
  • + +
  • When Heroku received the source code, it fetched all the dependencies in the Pipfile, creating a deployable slug.
  • +
  • The platform then spins up a dyno, a lightweight container that provides an isolated environment in which the slug can be mounted and executed.
  • +
  • You can scale your app, manage it, and deploy over 150 add-on services, from the Dashboard or CLI.
  • +
+
+
+

Next Steps

+
    +
  • If you are following the Getting Started guide, then please head back to the tutorial and follow the next steps!
  • +
  • If you deployed this app by deploying the Heroku Button, then in a command line shell, run:
  • +
      +
    • git clone https://github.com/heroku/python-getting-started.git - this will create a local copy of the source code for the app
    • +
    • cd python-getting-started - change directory into the local source code repository
    • +
    • heroku git:remote -a <your-app-name> - associate the Heroku app with the repository
    • +
    • You'll now be set up to run the app locally, or deploy changes to Heroku
    • +
    +
+

Helpful Links

+ +
+
+ +
+{% endblock %} diff --git a/hello/tests.py b/hello/tests.py new file mode 100644 index 0000000..1d69113 --- /dev/null +++ b/hello/tests.py @@ -0,0 +1,18 @@ +from django.contrib.auth.models import AnonymousUser, User +from django.test import TestCase, RequestFactory + +from .views import index + +class SimpleTest(TestCase): + def setUp(self): + # Every test needs access to the request factory. + self.factory = RequestFactory() + + def test_details(self): + # Create an instance of a GET request. + request = self.factory.get('/') + request.user = AnonymousUser() + + # Test my_view() as if it were deployed at /customer/details + response = index(request) + self.assertEqual(response.status_code, 200) \ No newline at end of file diff --git a/hello/views.py b/hello/views.py new file mode 100644 index 0000000..8558d84 --- /dev/null +++ b/hello/views.py @@ -0,0 +1,20 @@ +from django.shortcuts import render +from django.http import HttpResponse + +from .models import Greeting + +# Create your views here. +def index(request): + # return HttpResponse('Hello from Python!') + return render(request, 'index.html') + + +def db(request): + + greeting = Greeting() + greeting.save() + + greetings = Greeting.objects.all() + + return render(request, 'db.html', {'greetings': greetings}) +