├── .gitignore ├── README.md ├── frontend ├── .bowerrc ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jshintrc ├── .yo-rc.json ├── Gruntfile.js ├── README.md ├── app │ ├── .buildignore │ ├── .htaccess │ ├── 404.html │ ├── favicon.ico │ ├── images │ │ └── yeoman.png │ ├── index.html │ ├── robots.txt │ ├── scripts │ │ ├── app.js │ │ ├── controllers │ │ │ ├── about.js │ │ │ └── main.js │ │ └── services │ │ │ ├── server_url.js │ │ │ └── twitterprovider.js │ ├── styles │ │ └── main.css │ └── views │ │ ├── about.html │ │ └── main.html ├── bower.json ├── package.json └── test │ ├── .jshintrc │ ├── karma.conf.js │ └── spec │ ├── controllers │ ├── about.js │ └── main.js │ └── services │ ├── server_url.js │ └── twitterprovider.js └── twitter ├── .editorconfig ├── .foreverignore ├── .gitignore ├── .sailsrc ├── README.md ├── api ├── controllers │ ├── .gitkeep │ └── TweetController.js ├── models │ ├── .gitkeep │ └── Tweet.js ├── policies │ └── sessionAuth.js ├── responses │ ├── badRequest.js │ ├── forbidden.js │ ├── notFound.js │ ├── ok.js │ └── serverError.js └── services │ ├── .gitkeep │ ├── SentimentAnalysisService.js │ ├── TwitterService.js │ └── WordAnalysisService.js ├── app.js ├── config ├── blueprints.js ├── bootstrap.js ├── connections.js ├── cors.js ├── csrf.js ├── env │ ├── development.js │ └── production.js ├── globals.js ├── http.js ├── i18n.js ├── locales │ ├── _README.md │ ├── de.json │ ├── en.json │ ├── es.json │ └── fr.json ├── log.js ├── models.js ├── policies.js ├── routes.js ├── session.js ├── sockets.js └── views.js ├── package.json └── views ├── 403.ejs ├── 404.ejs ├── 500.ejs ├── homepage.ejs └── layout.ejs /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | /.idea 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | config.json 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # twitter-sentiment-analysis 2 | 3 | A novice's implementation of real-time twitter sentiment analysis. 4 | 5 | A working, live demo is available here: https://vishwajeetv.com/twitter 6 | 7 | Here's an informal blog post explaining how this is built : https://www.vishwajeetv.com/how-did-i-built-real-time-twitter-sentiment-analyser/ 8 | 9 | Demo Video - https://youtu.be/YEaFMTN4BlU 10 | 11 | Built with: 12 | * NodeJS (REST API with Sails.js) 13 | * MongoDB 14 | * Twitter API 15 | * sentiment (nodejs tool for sentiment analysis - AFINN based) 16 | * natural (Nodejs NLP toolkit) 17 | * AngularJS (frontend) 18 | * Firebase (real-time data storage / updation PaaS), AngularFire 19 | 20 | ## How to run 21 | * Obtain Twitter API usage tokens from Twitter API dashboard, set them in config.json 22 | * Create database 'twitter' in MongoDB 23 | * Run `cd twitter && sails lift` 24 | * Run `cd ../frontend && npm install && bower install` 25 | * Run `grunt serve` 26 | 27 | ## TODO 28 | * Daemon to fetch 100 tweets and saving them whenever allowed. 29 | 30 | ### Notes 31 | * For production setup, do this `echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p` 32 | * To install sails, use this `sudo npm install --unsafe-perm --verbose -g sails` 33 | * To start the production server, use `forever start app.js --prod` 34 | -------------------------------------------------------------------------------- /frontend/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /frontend/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /dist 3 | /.tmp 4 | /.sass-cache 5 | /bower_components 6 | -------------------------------------------------------------------------------- /frontend/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "browser": true, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "esnext": true, 7 | "latedef": true, 8 | "noarg": true, 9 | "node": true, 10 | "strict": true, 11 | "undef": true, 12 | "unused": true, 13 | "globals": { 14 | "angular": false 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /frontend/.yo-rc.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /frontend/Gruntfile.js: -------------------------------------------------------------------------------- 1 | // Generated on 2015-07-19 using generator-angular 0.12.1 2 | 'use strict'; 3 | 4 | // # Globbing 5 | // for performance reasons we're only matching one level down: 6 | // 'test/spec/{,*/}*.js' 7 | // use this if you want to recursively match all subfolders: 8 | // 'test/spec/**/*.js' 9 | 10 | module.exports = function (grunt) { 11 | 12 | // Time how long tasks take. Can help when optimizing build times 13 | require('time-grunt')(grunt); 14 | 15 | // Automatically load required Grunt tasks 16 | require('jit-grunt')(grunt, { 17 | useminPrepare: 'grunt-usemin', 18 | ngtemplates: 'grunt-angular-templates', 19 | cdnify: 'grunt-google-cdn' 20 | }); 21 | 22 | // Configurable paths for the application 23 | var appConfig = { 24 | app: require('./bower.json').appPath || 'app', 25 | dist: 'dist' 26 | }; 27 | 28 | // Define the configuration for all the tasks 29 | grunt.initConfig({ 30 | 31 | // Project settings 32 | yeoman: appConfig, 33 | 34 | // Watches files for changes and runs tasks based on the changed files 35 | watch: { 36 | bower: { 37 | files: ['bower.json'], 38 | tasks: ['wiredep'] 39 | }, 40 | js: { 41 | files: ['<%= yeoman.app %>/scripts/{,*/}*.js'], 42 | tasks: ['newer:jshint:all'], 43 | options: { 44 | livereload: '<%= connect.options.livereload %>' 45 | } 46 | }, 47 | jsTest: { 48 | files: ['test/spec/{,*/}*.js'], 49 | tasks: ['newer:jshint:test', 'karma'] 50 | }, 51 | styles: { 52 | files: ['<%= yeoman.app %>/styles/{,*/}*.css'], 53 | tasks: ['newer:copy:styles', 'autoprefixer'] 54 | }, 55 | gruntfile: { 56 | files: ['Gruntfile.js'] 57 | }, 58 | livereload: { 59 | options: { 60 | livereload: '<%= connect.options.livereload %>' 61 | }, 62 | files: [ 63 | '<%= yeoman.app %>/{,*/}*.html', 64 | '.tmp/styles/{,*/}*.css', 65 | '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}' 66 | ] 67 | } 68 | }, 69 | 70 | // The actual grunt server settings 71 | connect: { 72 | options: { 73 | port: 9000, 74 | // Change this to '0.0.0.0' to access the server from outside. 75 | hostname: 'localhost', 76 | livereload: 35729 77 | }, 78 | livereload: { 79 | options: { 80 | open: true, 81 | middleware: function (connect) { 82 | return [ 83 | connect.static('.tmp'), 84 | connect().use( 85 | '/bower_components', 86 | connect.static('./bower_components') 87 | ), 88 | connect().use( 89 | '/app/styles', 90 | connect.static('./app/styles') 91 | ), 92 | connect.static(appConfig.app) 93 | ]; 94 | } 95 | } 96 | }, 97 | test: { 98 | options: { 99 | port: 9001, 100 | middleware: function (connect) { 101 | return [ 102 | connect.static('.tmp'), 103 | connect.static('test'), 104 | connect().use( 105 | '/bower_components', 106 | connect.static('./bower_components') 107 | ), 108 | connect.static(appConfig.app) 109 | ]; 110 | } 111 | } 112 | }, 113 | dist: { 114 | options: { 115 | open: true, 116 | base: '<%= yeoman.dist %>' 117 | } 118 | } 119 | }, 120 | 121 | // Make sure code styles are up to par and there are no obvious mistakes 122 | jshint: { 123 | options: { 124 | jshintrc: '.jshintrc', 125 | reporter: require('jshint-stylish') 126 | }, 127 | all: { 128 | src: [ 129 | 'Gruntfile.js', 130 | '<%= yeoman.app %>/scripts/{,*/}*.js' 131 | ] 132 | }, 133 | test: { 134 | options: { 135 | jshintrc: 'test/.jshintrc' 136 | }, 137 | src: ['test/spec/{,*/}*.js'] 138 | } 139 | }, 140 | 141 | // Empties folders to start fresh 142 | clean: { 143 | dist: { 144 | files: [{ 145 | dot: true, 146 | src: [ 147 | '.tmp', 148 | '<%= yeoman.dist %>/{,*/}*', 149 | '!<%= yeoman.dist %>/.git{,*/}*' 150 | ] 151 | }] 152 | }, 153 | server: '.tmp' 154 | }, 155 | 156 | // Add vendor prefixed styles 157 | autoprefixer: { 158 | options: { 159 | browsers: ['last 1 version'] 160 | }, 161 | server: { 162 | options: { 163 | map: true, 164 | }, 165 | files: [{ 166 | expand: true, 167 | cwd: '.tmp/styles/', 168 | src: '{,*/}*.css', 169 | dest: '.tmp/styles/' 170 | }] 171 | }, 172 | dist: { 173 | files: [{ 174 | expand: true, 175 | cwd: '.tmp/styles/', 176 | src: '{,*/}*.css', 177 | dest: '.tmp/styles/' 178 | }] 179 | } 180 | }, 181 | 182 | // Automatically inject Bower components into the app 183 | wiredep: { 184 | app: { 185 | src: ['<%= yeoman.app %>/index.html'], 186 | ignorePath: /\.\.\// 187 | }, 188 | test: { 189 | devDependencies: true, 190 | src: '<%= karma.unit.configFile %>', 191 | ignorePath: /\.\.\//, 192 | fileTypes:{ 193 | js: { 194 | block: /(([\s\t]*)\/{2}\s*?bower:\s*?(\S*))(\n|\r|.)*?(\/{2}\s*endbower)/gi, 195 | detect: { 196 | js: /'(.*\.js)'/gi 197 | }, 198 | replace: { 199 | js: '\'{{filePath}}\',' 200 | } 201 | } 202 | } 203 | } 204 | }, 205 | 206 | // Renames files for browser caching purposes 207 | filerev: { 208 | dist: { 209 | src: [ 210 | '<%= yeoman.dist %>/scripts/{,*/}*.js', 211 | '<%= yeoman.dist %>/styles/{,*/}*.css', 212 | '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', 213 | '<%= yeoman.dist %>/styles/fonts/*' 214 | ] 215 | } 216 | }, 217 | 218 | // Reads HTML for usemin blocks to enable smart builds that automatically 219 | // concat, minify and revision files. Creates configurations in memory so 220 | // additional tasks can operate on them 221 | useminPrepare: { 222 | html: '<%= yeoman.app %>/index.html', 223 | options: { 224 | dest: '<%= yeoman.dist %>', 225 | flow: { 226 | html: { 227 | steps: { 228 | js: ['concat', 'uglifyjs'], 229 | css: ['cssmin'] 230 | }, 231 | post: {} 232 | } 233 | } 234 | } 235 | }, 236 | 237 | // Performs rewrites based on filerev and the useminPrepare configuration 238 | usemin: { 239 | html: ['<%= yeoman.dist %>/{,*/}*.html'], 240 | css: ['<%= yeoman.dist %>/styles/{,*/}*.css'], 241 | js: ['<%= yeoman.dist %>/scripts/{,*/}*.js'], 242 | options: { 243 | assetsDirs: [ 244 | '<%= yeoman.dist %>', 245 | '<%= yeoman.dist %>/images', 246 | '<%= yeoman.dist %>/styles' 247 | ], 248 | patterns: { 249 | js: [[/(images\/[^''""]*\.(png|jpg|jpeg|gif|webp|svg))/g, 'Replacing references to images']] 250 | } 251 | } 252 | }, 253 | 254 | // The following *-min tasks will produce minified files in the dist folder 255 | // By default, your `index.html`'s will take care of 256 | // minification. These next options are pre-configured if you do not wish 257 | // to use the Usemin blocks. 258 | // cssmin: { 259 | // dist: { 260 | // files: { 261 | // '<%= yeoman.dist %>/styles/main.css': [ 262 | // '.tmp/styles/{,*/}*.css' 263 | // ] 264 | // } 265 | // } 266 | // }, 267 | // uglify: { 268 | // dist: { 269 | // files: { 270 | // '<%= yeoman.dist %>/scripts/scripts.js': [ 271 | // '<%= yeoman.dist %>/scripts/scripts.js' 272 | // ] 273 | // } 274 | // } 275 | // }, 276 | // concat: { 277 | // dist: {} 278 | // }, 279 | 280 | imagemin: { 281 | dist: { 282 | files: [{ 283 | expand: true, 284 | cwd: '<%= yeoman.app %>/images', 285 | src: '{,*/}*.{png,jpg,jpeg,gif}', 286 | dest: '<%= yeoman.dist %>/images' 287 | }] 288 | } 289 | }, 290 | 291 | svgmin: { 292 | dist: { 293 | files: [{ 294 | expand: true, 295 | cwd: '<%= yeoman.app %>/images', 296 | src: '{,*/}*.svg', 297 | dest: '<%= yeoman.dist %>/images' 298 | }] 299 | } 300 | }, 301 | 302 | htmlmin: { 303 | dist: { 304 | options: { 305 | collapseWhitespace: true, 306 | conservativeCollapse: true, 307 | collapseBooleanAttributes: true, 308 | removeCommentsFromCDATA: true 309 | }, 310 | files: [{ 311 | expand: true, 312 | cwd: '<%= yeoman.dist %>', 313 | src: ['*.html'], 314 | dest: '<%= yeoman.dist %>' 315 | }] 316 | } 317 | }, 318 | 319 | ngtemplates: { 320 | dist: { 321 | options: { 322 | module: 'twitterAppApp', 323 | htmlmin: '<%= htmlmin.dist.options %>', 324 | usemin: 'scripts/scripts.js' 325 | }, 326 | cwd: '<%= yeoman.app %>', 327 | src: 'views/{,*/}*.html', 328 | dest: '.tmp/templateCache.js' 329 | } 330 | }, 331 | 332 | // ng-annotate tries to make the code safe for minification automatically 333 | // by using the Angular long form for dependency injection. 334 | ngAnnotate: { 335 | dist: { 336 | files: [{ 337 | expand: true, 338 | cwd: '.tmp/concat/scripts', 339 | src: '*.js', 340 | dest: '.tmp/concat/scripts' 341 | }] 342 | } 343 | }, 344 | 345 | // Replace Google CDN references 346 | cdnify: { 347 | dist: { 348 | html: ['<%= yeoman.dist %>/*.html'] 349 | } 350 | }, 351 | 352 | // Copies remaining files to places other tasks can use 353 | copy: { 354 | dist: { 355 | files: [{ 356 | expand: true, 357 | dot: true, 358 | cwd: '<%= yeoman.app %>', 359 | dest: '<%= yeoman.dist %>', 360 | src: [ 361 | '*.{ico,png,txt}', 362 | '.htaccess', 363 | '*.html', 364 | 'images/{,*/}*.{webp}', 365 | 'styles/fonts/{,*/}*.*' 366 | ] 367 | }, { 368 | expand: true, 369 | cwd: '.tmp/images', 370 | dest: '<%= yeoman.dist %>/images', 371 | src: ['generated/*'] 372 | }, { 373 | expand: true, 374 | cwd: 'bower_components/bootstrap/dist', 375 | src: 'fonts/*', 376 | dest: '<%= yeoman.dist %>' 377 | }] 378 | }, 379 | styles: { 380 | expand: true, 381 | cwd: '<%= yeoman.app %>/styles', 382 | dest: '.tmp/styles/', 383 | src: '{,*/}*.css' 384 | } 385 | }, 386 | 387 | // Run some tasks in parallel to speed up the build process 388 | concurrent: { 389 | server: [ 390 | 'copy:styles' 391 | ], 392 | test: [ 393 | 'copy:styles' 394 | ], 395 | dist: [ 396 | 'copy:styles', 397 | 'imagemin', 398 | 'svgmin' 399 | ] 400 | }, 401 | 402 | // Test settings 403 | karma: { 404 | unit: { 405 | configFile: 'test/karma.conf.js', 406 | singleRun: true 407 | } 408 | } 409 | }); 410 | 411 | 412 | grunt.registerTask('serve', 'Compile then start a connect web server', function (target) { 413 | if (target === 'dist') { 414 | return grunt.task.run(['build', 'connect:dist:keepalive']); 415 | } 416 | 417 | grunt.task.run([ 418 | 'clean:server', 419 | 'wiredep', 420 | 'concurrent:server', 421 | 'autoprefixer:server', 422 | 'connect:livereload', 423 | 'watch' 424 | ]); 425 | }); 426 | 427 | grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) { 428 | grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.'); 429 | grunt.task.run(['serve:' + target]); 430 | }); 431 | 432 | grunt.registerTask('test', [ 433 | 'clean:server', 434 | 'wiredep', 435 | 'concurrent:test', 436 | 'autoprefixer', 437 | 'connect:test', 438 | 'karma' 439 | ]); 440 | 441 | grunt.registerTask('build', [ 442 | 'clean:dist', 443 | 'wiredep', 444 | 'useminPrepare', 445 | 'concurrent:dist', 446 | 'autoprefixer', 447 | 'ngtemplates', 448 | 'concat', 449 | 'ngAnnotate', 450 | 'copy:dist', 451 | 'cdnify', 452 | 'cssmin', 453 | 'uglify', 454 | 'filerev', 455 | 'usemin', 456 | 'htmlmin' 457 | ]); 458 | 459 | grunt.registerTask('default', [ 460 | 'newer:jshint', 461 | 'test', 462 | 'build' 463 | ]); 464 | }; 465 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # twitter-app 2 | 3 | This project is generated with [yo angular generator](https://github.com/yeoman/generator-angular) 4 | version 0.12.1. 5 | 6 | ## Build & development 7 | 8 | Run `grunt` for building and `grunt serve` for preview. 9 | 10 | ## Testing 11 | 12 | Running `grunt test` will run the unit tests with karma. 13 | -------------------------------------------------------------------------------- /frontend/app/.buildignore: -------------------------------------------------------------------------------- 1 | *.coffee -------------------------------------------------------------------------------- /frontend/app/.htaccess: -------------------------------------------------------------------------------- 1 | # Apache Configuration File 2 | 3 | # (!) Using `.htaccess` files slows down Apache, therefore, if you have access 4 | # to the main server config file (usually called `httpd.conf`), you should add 5 | # this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html. 6 | 7 | # ############################################################################## 8 | # # CROSS-ORIGIN RESOURCE SHARING (CORS) # 9 | # ############################################################################## 10 | 11 | # ------------------------------------------------------------------------------ 12 | # | Cross-domain AJAX requests | 13 | # ------------------------------------------------------------------------------ 14 | 15 | # Enable cross-origin AJAX requests. 16 | # http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity 17 | # http://enable-cors.org/ 18 | 19 | # 20 | # Header set Access-Control-Allow-Origin "*" 21 | # 22 | 23 | # ------------------------------------------------------------------------------ 24 | # | CORS-enabled images | 25 | # ------------------------------------------------------------------------------ 26 | 27 | # Send the CORS header for images when browsers request it. 28 | # https://developer.mozilla.org/en/CORS_Enabled_Image 29 | # http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html 30 | # http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/ 31 | 32 | 33 | 34 | 35 | SetEnvIf Origin ":" IS_CORS 36 | Header set Access-Control-Allow-Origin "*" env=IS_CORS 37 | 38 | 39 | 40 | 41 | # ------------------------------------------------------------------------------ 42 | # | Web fonts access | 43 | # ------------------------------------------------------------------------------ 44 | 45 | # Allow access from all domains for web fonts 46 | 47 | 48 | 49 | Header set Access-Control-Allow-Origin "*" 50 | 51 | 52 | 53 | 54 | # ############################################################################## 55 | # # ERRORS # 56 | # ############################################################################## 57 | 58 | # ------------------------------------------------------------------------------ 59 | # | 404 error prevention for non-existing redirected folders | 60 | # ------------------------------------------------------------------------------ 61 | 62 | # Prevent Apache from returning a 404 error for a rewrite if a directory 63 | # with the same name does not exist. 64 | # http://httpd.apache.org/docs/current/content-negotiation.html#multiviews 65 | # http://www.webmasterworld.com/apache/3808792.htm 66 | 67 | Options -MultiViews 68 | 69 | # ------------------------------------------------------------------------------ 70 | # | Custom error messages / pages | 71 | # ------------------------------------------------------------------------------ 72 | 73 | # You can customize what Apache returns to the client in case of an error (see 74 | # http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.: 75 | 76 | ErrorDocument 404 /404.html 77 | 78 | 79 | # ############################################################################## 80 | # # INTERNET EXPLORER # 81 | # ############################################################################## 82 | 83 | # ------------------------------------------------------------------------------ 84 | # | Better website experience | 85 | # ------------------------------------------------------------------------------ 86 | 87 | # Force IE to render pages in the highest available mode in the various 88 | # cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf. 89 | 90 | 91 | Header set X-UA-Compatible "IE=edge" 92 | # `mod_headers` can't match based on the content-type, however, we only 93 | # want to send this header for HTML pages and not for the other resources 94 | 95 | Header unset X-UA-Compatible 96 | 97 | 98 | 99 | # ------------------------------------------------------------------------------ 100 | # | Cookie setting from iframes | 101 | # ------------------------------------------------------------------------------ 102 | 103 | # Allow cookies to be set from iframes in IE. 104 | 105 | # 106 | # Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"" 107 | # 108 | 109 | # ------------------------------------------------------------------------------ 110 | # | Screen flicker | 111 | # ------------------------------------------------------------------------------ 112 | 113 | # Stop screen flicker in IE on CSS rollovers (this only works in 114 | # combination with the `ExpiresByType` directives for images from below). 115 | 116 | # BrowserMatch "MSIE" brokenvary=1 117 | # BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1 118 | # BrowserMatch "Opera" !brokenvary 119 | # SetEnvIf brokenvary 1 force-no-vary 120 | 121 | 122 | # ############################################################################## 123 | # # MIME TYPES AND ENCODING # 124 | # ############################################################################## 125 | 126 | # ------------------------------------------------------------------------------ 127 | # | Proper MIME types for all files | 128 | # ------------------------------------------------------------------------------ 129 | 130 | 131 | 132 | # Audio 133 | AddType audio/mp4 m4a f4a f4b 134 | AddType audio/ogg oga ogg 135 | 136 | # JavaScript 137 | # Normalize to standard type (it's sniffed in IE anyways): 138 | # http://tools.ietf.org/html/rfc4329#section-7.2 139 | AddType application/javascript js jsonp 140 | AddType application/json json 141 | 142 | # Video 143 | AddType video/mp4 mp4 m4v f4v f4p 144 | AddType video/ogg ogv 145 | AddType video/webm webm 146 | AddType video/x-flv flv 147 | 148 | # Web fonts 149 | AddType application/font-woff woff 150 | AddType application/vnd.ms-fontobject eot 151 | 152 | # Browsers usually ignore the font MIME types and sniff the content, 153 | # however, Chrome shows a warning if other MIME types are used for the 154 | # following fonts. 155 | AddType application/x-font-ttf ttc ttf 156 | AddType font/opentype otf 157 | 158 | # Make SVGZ fonts work on iPad: 159 | # https://twitter.com/FontSquirrel/status/14855840545 160 | AddType image/svg+xml svg svgz 161 | AddEncoding gzip svgz 162 | 163 | # Other 164 | AddType application/octet-stream safariextz 165 | AddType application/x-chrome-extension crx 166 | AddType application/x-opera-extension oex 167 | AddType application/x-shockwave-flash swf 168 | AddType application/x-web-app-manifest+json webapp 169 | AddType application/x-xpinstall xpi 170 | AddType application/xml atom rdf rss xml 171 | AddType image/webp webp 172 | AddType image/x-icon ico 173 | AddType text/cache-manifest appcache manifest 174 | AddType text/vtt vtt 175 | AddType text/x-component htc 176 | AddType text/x-vcard vcf 177 | 178 | 179 | 180 | # ------------------------------------------------------------------------------ 181 | # | UTF-8 encoding | 182 | # ------------------------------------------------------------------------------ 183 | 184 | # Use UTF-8 encoding for anything served as `text/html` or `text/plain`. 185 | AddDefaultCharset utf-8 186 | 187 | # Force UTF-8 for certain file formats. 188 | 189 | AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml 190 | 191 | 192 | 193 | # ############################################################################## 194 | # # URL REWRITES # 195 | # ############################################################################## 196 | 197 | # ------------------------------------------------------------------------------ 198 | # | Rewrite engine | 199 | # ------------------------------------------------------------------------------ 200 | 201 | # Turning on the rewrite engine and enabling the `FollowSymLinks` option is 202 | # necessary for the following directives to work. 203 | 204 | # If your web host doesn't allow the `FollowSymlinks` option, you may need to 205 | # comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the 206 | # performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks 207 | 208 | # Also, some cloud hosting services require `RewriteBase` to be set: 209 | # http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site 210 | 211 | 212 | Options +FollowSymlinks 213 | # Options +SymLinksIfOwnerMatch 214 | RewriteEngine On 215 | # RewriteBase / 216 | 217 | 218 | # ------------------------------------------------------------------------------ 219 | # | Suppressing / Forcing the "www." at the beginning of URLs | 220 | # ------------------------------------------------------------------------------ 221 | 222 | # The same content should never be available under two different URLs especially 223 | # not with and without "www." at the beginning. This can cause SEO problems 224 | # (duplicate content), therefore, you should choose one of the alternatives and 225 | # redirect the other one. 226 | 227 | # By default option 1 (no "www.") is activated: 228 | # http://no-www.org/faq.php?q=class_b 229 | 230 | # If you'd prefer to use option 2, just comment out all the lines from option 1 231 | # and uncomment the ones from option 2. 232 | 233 | # IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME! 234 | 235 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 236 | 237 | # Option 1: rewrite www.example.com → example.com 238 | 239 | 240 | RewriteCond %{HTTPS} !=on 241 | RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] 242 | RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L] 243 | 244 | 245 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 246 | 247 | # Option 2: rewrite example.com → www.example.com 248 | 249 | # Be aware that the following might not be a good idea if you use "real" 250 | # subdomains for certain parts of your website. 251 | 252 | # 253 | # RewriteCond %{HTTPS} !=on 254 | # RewriteCond %{HTTP_HOST} !^www\..+$ [NC] 255 | # RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L] 256 | # 257 | 258 | 259 | # ############################################################################## 260 | # # SECURITY # 261 | # ############################################################################## 262 | 263 | # ------------------------------------------------------------------------------ 264 | # | Content Security Policy (CSP) | 265 | # ------------------------------------------------------------------------------ 266 | 267 | # You can mitigate the risk of cross-site scripting and other content-injection 268 | # attacks by setting a Content Security Policy which whitelists trusted sources 269 | # of content for your site. 270 | 271 | # The example header below allows ONLY scripts that are loaded from the current 272 | # site's origin (no inline scripts, no CDN, etc). This almost certainly won't 273 | # work as-is for your site! 274 | 275 | # To get all the details you'll need to craft a reasonable policy for your site, 276 | # read: http://html5rocks.com/en/tutorials/security/content-security-policy (or 277 | # see the specification: http://w3.org/TR/CSP). 278 | 279 | # 280 | # Header set Content-Security-Policy "script-src 'self'; object-src 'self'" 281 | # 282 | # Header unset Content-Security-Policy 283 | # 284 | # 285 | 286 | # ------------------------------------------------------------------------------ 287 | # | File access | 288 | # ------------------------------------------------------------------------------ 289 | 290 | # Block access to directories without a default document. 291 | # Usually you should leave this uncommented because you shouldn't allow anyone 292 | # to surf through every directory on your server (which may includes rather 293 | # private places like the CMS's directories). 294 | 295 | 296 | Options -Indexes 297 | 298 | 299 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 300 | 301 | # Block access to hidden files and directories. 302 | # This includes directories used by version control systems such as Git and SVN. 303 | 304 | 305 | RewriteCond %{SCRIPT_FILENAME} -d [OR] 306 | RewriteCond %{SCRIPT_FILENAME} -f 307 | RewriteRule "(^|/)\." - [F] 308 | 309 | 310 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 311 | 312 | # Block access to backup and source files. 313 | # These files may be left by some text editors and can pose a great security 314 | # danger when anyone has access to them. 315 | 316 | 317 | Order allow,deny 318 | Deny from all 319 | Satisfy All 320 | 321 | 322 | # ------------------------------------------------------------------------------ 323 | # | Secure Sockets Layer (SSL) | 324 | # ------------------------------------------------------------------------------ 325 | 326 | # Rewrite secure requests properly to prevent SSL certificate warnings, e.g.: 327 | # prevent `https://www.example.com` when your certificate only allows 328 | # `https://secure.example.com`. 329 | 330 | # 331 | # RewriteCond %{SERVER_PORT} !^443 332 | # RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L] 333 | # 334 | 335 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 336 | 337 | # Force client-side SSL redirection. 338 | 339 | # If a user types "example.com" in his browser, the above rule will redirect him 340 | # to the secure version of the site. That still leaves a window of opportunity 341 | # (the initial HTTP connection) for an attacker to downgrade or redirect the 342 | # request. The following header ensures that browser will ONLY connect to your 343 | # server via HTTPS, regardless of what the users type in the address bar. 344 | # http://www.html5rocks.com/en/tutorials/security/transport-layer-security/ 345 | 346 | # 347 | # Header set Strict-Transport-Security max-age=16070400; 348 | # 349 | 350 | # ------------------------------------------------------------------------------ 351 | # | Server software information | 352 | # ------------------------------------------------------------------------------ 353 | 354 | # Avoid displaying the exact Apache version number, the description of the 355 | # generic OS-type and the information about Apache's compiled-in modules. 356 | 357 | # ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`! 358 | 359 | # ServerTokens Prod 360 | 361 | 362 | # ############################################################################## 363 | # # WEB PERFORMANCE # 364 | # ############################################################################## 365 | 366 | # ------------------------------------------------------------------------------ 367 | # | Compression | 368 | # ------------------------------------------------------------------------------ 369 | 370 | 371 | 372 | # Force compression for mangled headers. 373 | # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping 374 | 375 | 376 | SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding 377 | RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding 378 | 379 | 380 | 381 | # Compress all output labeled with one of the following MIME-types 382 | # (for Apache versions below 2.3.7, you don't need to enable `mod_filter` 383 | # and can remove the `` and `` lines 384 | # as `AddOutputFilterByType` is still in the core directives). 385 | 386 | AddOutputFilterByType DEFLATE application/atom+xml \ 387 | application/javascript \ 388 | application/json \ 389 | application/rss+xml \ 390 | application/vnd.ms-fontobject \ 391 | application/x-font-ttf \ 392 | application/x-web-app-manifest+json \ 393 | application/xhtml+xml \ 394 | application/xml \ 395 | font/opentype \ 396 | image/svg+xml \ 397 | image/x-icon \ 398 | text/css \ 399 | text/html \ 400 | text/plain \ 401 | text/x-component \ 402 | text/xml 403 | 404 | 405 | 406 | 407 | # ------------------------------------------------------------------------------ 408 | # | Content transformations | 409 | # ------------------------------------------------------------------------------ 410 | 411 | # Prevent some of the mobile network providers from modifying the content of 412 | # your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5. 413 | 414 | # 415 | # Header set Cache-Control "no-transform" 416 | # 417 | 418 | # ------------------------------------------------------------------------------ 419 | # | ETag removal | 420 | # ------------------------------------------------------------------------------ 421 | 422 | # Since we're sending far-future expires headers (see below), ETags can 423 | # be removed: http://developer.yahoo.com/performance/rules.html#etags. 424 | 425 | # `FileETag None` is not enough for every server. 426 | 427 | Header unset ETag 428 | 429 | 430 | FileETag None 431 | 432 | # ------------------------------------------------------------------------------ 433 | # | Expires headers (for better cache control) | 434 | # ------------------------------------------------------------------------------ 435 | 436 | # The following expires headers are set pretty far in the future. If you don't 437 | # control versioning with filename-based cache busting, consider lowering the 438 | # cache time for resources like CSS and JS to something like 1 week. 439 | 440 | 441 | 442 | ExpiresActive on 443 | ExpiresDefault "access plus 1 month" 444 | 445 | # CSS 446 | ExpiresByType text/css "access plus 1 year" 447 | 448 | # Data interchange 449 | ExpiresByType application/json "access plus 0 seconds" 450 | ExpiresByType application/xml "access plus 0 seconds" 451 | ExpiresByType text/xml "access plus 0 seconds" 452 | 453 | # Favicon (cannot be renamed!) 454 | ExpiresByType image/x-icon "access plus 1 week" 455 | 456 | # HTML components (HTCs) 457 | ExpiresByType text/x-component "access plus 1 month" 458 | 459 | # HTML 460 | ExpiresByType text/html "access plus 0 seconds" 461 | 462 | # JavaScript 463 | ExpiresByType application/javascript "access plus 1 year" 464 | 465 | # Manifest files 466 | ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds" 467 | ExpiresByType text/cache-manifest "access plus 0 seconds" 468 | 469 | # Media 470 | ExpiresByType audio/ogg "access plus 1 month" 471 | ExpiresByType image/gif "access plus 1 month" 472 | ExpiresByType image/jpeg "access plus 1 month" 473 | ExpiresByType image/png "access plus 1 month" 474 | ExpiresByType video/mp4 "access plus 1 month" 475 | ExpiresByType video/ogg "access plus 1 month" 476 | ExpiresByType video/webm "access plus 1 month" 477 | 478 | # Web feeds 479 | ExpiresByType application/atom+xml "access plus 1 hour" 480 | ExpiresByType application/rss+xml "access plus 1 hour" 481 | 482 | # Web fonts 483 | ExpiresByType application/font-woff "access plus 1 month" 484 | ExpiresByType application/vnd.ms-fontobject "access plus 1 month" 485 | ExpiresByType application/x-font-ttf "access plus 1 month" 486 | ExpiresByType font/opentype "access plus 1 month" 487 | ExpiresByType image/svg+xml "access plus 1 month" 488 | 489 | 490 | 491 | # ------------------------------------------------------------------------------ 492 | # | Filename-based cache busting | 493 | # ------------------------------------------------------------------------------ 494 | 495 | # If you're not using a build process to manage your filename version revving, 496 | # you might want to consider enabling the following directives to route all 497 | # requests such as `/css/style.12345.css` to `/css/style.css`. 498 | 499 | # To understand why this is important and a better idea than `*.css?v231`, read: 500 | # http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring 501 | 502 | # 503 | # RewriteCond %{REQUEST_FILENAME} !-f 504 | # RewriteCond %{REQUEST_FILENAME} !-d 505 | # RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L] 506 | # 507 | 508 | # ------------------------------------------------------------------------------ 509 | # | File concatenation | 510 | # ------------------------------------------------------------------------------ 511 | 512 | # Allow concatenation from within specific CSS and JS files, e.g.: 513 | # Inside of `script.combined.js` you could have 514 | # 515 | # 516 | # and they would be included into this single file. 517 | 518 | # 519 | # 520 | # Options +Includes 521 | # AddOutputFilterByType INCLUDES application/javascript application/json 522 | # SetOutputFilter INCLUDES 523 | # 524 | # 525 | # Options +Includes 526 | # AddOutputFilterByType INCLUDES text/css 527 | # SetOutputFilter INCLUDES 528 | # 529 | # 530 | 531 | # ------------------------------------------------------------------------------ 532 | # | Persistent connections | 533 | # ------------------------------------------------------------------------------ 534 | 535 | # Allow multiple requests to be sent over the same TCP connection: 536 | # http://httpd.apache.org/docs/current/en/mod/core.html#keepalive. 537 | 538 | # Enable if you serve a lot of static content but, be aware of the 539 | # possible disadvantages! 540 | 541 | # 542 | # Header set Connection Keep-Alive 543 | # 544 | -------------------------------------------------------------------------------- /frontend/app/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found :( 6 | 136 | 137 | 138 |
139 |

Not found :(

140 |

Sorry, but the page you were trying to view does not exist.

141 |

It looks like this was the result of either:

142 | 146 | 149 | 150 |
151 | 152 | 153 | -------------------------------------------------------------------------------- /frontend/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vishwajeetv/twitter-sentiment-analysis/2a31c537ff6e037d2e693c7c3d2cde858a7142ee/frontend/app/favicon.ico -------------------------------------------------------------------------------- /frontend/app/images/yeoman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vishwajeetv/twitter-sentiment-analysis/2a31c537ff6e037d2e693c7c3d2cde858a7142ee/frontend/app/images/yeoman.png -------------------------------------------------------------------------------- /frontend/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 25 | 26 | 27 |
28 | 66 |
67 | 68 |
69 |
70 |
71 | 72 | 78 | 79 | 80 | 81 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /frontend/app/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | Disallow: 5 | -------------------------------------------------------------------------------- /frontend/app/scripts/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc overview 5 | * @name twitterAppApp 6 | * @description 7 | * # twitterAppApp 8 | * 9 | * Main module of the application. 10 | */ 11 | angular 12 | .module('twitterAppApp', [ 13 | 'toaster', 14 | 'angular-loading-bar', 15 | 'ngAnimate', 16 | 'ngCookies', 17 | 'ngResource', 18 | 'ngRoute', 19 | 'ngSanitize', 20 | 'ngTouch', 21 | 'restangular','firebase' 22 | ]). 23 | config(function (RestangularProvider, SERVER_URL) { 24 | RestangularProvider.setBaseUrl(SERVER_URL); 25 | RestangularProvider.setDefaultHeaders({"Content-Type": "application/json"}); 26 | }) 27 | .config(function ($routeProvider) { 28 | $routeProvider 29 | .when('/', { 30 | templateUrl: 'views/main.html', 31 | controller: 'MainCtrl', 32 | controllerAs: 'main' 33 | }) 34 | .when('/about', { 35 | templateUrl: 'views/about.html', 36 | controller: 'AboutCtrl', 37 | controllerAs: 'about' 38 | }) 39 | .otherwise({ 40 | redirectTo: '/' 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /frontend/app/scripts/controllers/about.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name twitterAppApp.controller:AboutCtrl 6 | * @description 7 | * # AboutCtrl 8 | * Controller of the twitterAppApp 9 | */ 10 | angular.module('twitterAppApp') 11 | .controller('AboutCtrl', function () { 12 | this.awesomeThings = [ 13 | 'HTML5 Boilerplate', 14 | 'AngularJS', 15 | 'Karma' 16 | ]; 17 | }); 18 | -------------------------------------------------------------------------------- /frontend/app/scripts/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name twitterAppApp.controller:MainCtrl 6 | * @description 7 | * # MainCtrl 8 | * Controller of the twitterAppApp 9 | */ 10 | angular.module('twitterAppApp') 11 | .controller('MainCtrl', function ($scope, Restangular, TwitterProvider, $firebaseObject, toaster) { 12 | 13 | var ref = firebase.database().ref().child("data"); 14 | var syncObject = $firebaseObject(ref); 15 | syncObject.$bindTo($scope, "tweetsData"); 16 | $scope.tweets = {}; 17 | $scope.positiveCount = 0; 18 | $scope.negativeCount = 0; 19 | $scope.totalScore = 0; 20 | $scope.numberOfTweets = 0; 21 | $scope.averageScore = 0; 22 | 23 | $scope.setSearchText = function(text) 24 | { 25 | console.log(text); 26 | $scope.searchText = (text); 27 | }; 28 | 29 | 30 | $scope.sentiment = null; 31 | $scope.search = function() 32 | { 33 | if( ($scope.searchText == undefined) || ($scope.searchText == '')) 34 | { 35 | toaster.pop('error', "No Search Query", "Please enter the search query"); 36 | } 37 | else 38 | { 39 | 40 | $scope.searchText = encodeURIComponent(decodeURIComponent($scope.searchText)); 41 | var searchText = $scope.searchText; 42 | toaster.pop('success', "Analysis Started", "Enjoy the magic!"); 43 | TwitterProvider.getTweets((searchText)).then( 44 | function( tweetsData ) 45 | { 46 | 47 | }) 48 | 49 | } 50 | 51 | 52 | 53 | } 54 | 55 | 56 | $scope.trends = {}; 57 | $scope.getTrends = function(globalLocationId, localLocationId) 58 | { 59 | TwitterProvider.getTrends(globalLocationId).then( 60 | function( trends ) 61 | { 62 | $scope.globalTrends = trends; 63 | } 64 | ) 65 | TwitterProvider.getTrends(localLocationId).then( 66 | function( trends ) 67 | { 68 | $scope.localTrends = trends; 69 | } 70 | ) 71 | }; 72 | 73 | $scope.getSentimentClass = function() 74 | { 75 | if(!$scope.searchText) 76 | return null; 77 | if(!($scope.tweetsData)) 78 | return null; 79 | if(!($scope.tweetsData[$scope.searchText])) 80 | return null; 81 | 82 | if($scope.tweetsData[$scope.searchText]) 83 | var sentiment = $scope.tweetsData[$scope.searchText].overallAnalysis.sentiment; 84 | if((sentiment == 'Positive') || (sentiment == 'Very Positive')) 85 | { 86 | return 'fa text-success fa-5x fa-smile-o' 87 | } 88 | else if((sentiment == 'Negative') || (sentiment == 'Very Negative')) 89 | { 90 | return 'fa text-danger fa-5x fa-frown-o' 91 | } 92 | else 93 | { 94 | return 'fa text-warning fa-5x fa-meh-o' 95 | } 96 | }; 97 | 98 | function init() 99 | { 100 | $scope.getTrends(1, 23424848); 101 | } 102 | 103 | init(); 104 | 105 | }); 106 | -------------------------------------------------------------------------------- /frontend/app/scripts/services/server_url.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc service 5 | * @name twitterAppApp.SERVERURL 6 | * @description 7 | * # SERVERURL 8 | * Constant in the twitterAppApp. 9 | */ 10 | angular.module('twitterAppApp') 11 | //.constant('SERVER_URL', 'http://localhost:1337/'); 12 | .constant('SERVER_URL', 'http://128.199.112.23:1337/'); 13 | -------------------------------------------------------------------------------- /frontend/app/scripts/services/twitterprovider.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc service 5 | * @name twitterAppApp.TwitterProvider 6 | * @description 7 | * # TwitterProvider 8 | * Service in the twitterAppApp. 9 | */ 10 | angular.module('twitterAppApp') 11 | .service('TwitterProvider', function ($http, $q, SERVER_URL) { 12 | this.getTweets = function (searchText) { 13 | var url = SERVER_URL + 'tweet/getAnalyzedTweets/?query='+searchText; 14 | var deferred = $q.defer(); 15 | $http.get(url, {timeout: 0}).then(function (response, status, headers, config) { 16 | deferred.resolve(response.data); 17 | },function (response, status, headers, config) { 18 | deferred.reject(); 19 | console.log("could not retrieve tweets"); 20 | }); 21 | return deferred.promise; 22 | }; 23 | 24 | this.getTrends = function (locationId) { 25 | var url = SERVER_URL + 'tweet/getTrends/?location='+locationId; 26 | var deferred = $q.defer(); 27 | $http.get(url). 28 | then(function (response, status, headers, config) { 29 | if(typeof response.data != 'undefined') 30 | { 31 | if(!response.data.error) 32 | { 33 | deferred.resolve(response.data.trends[0].trends); 34 | } 35 | } 36 | },function (data, status, headers, config) { 37 | deferred.reject(); 38 | console.log("could not retrieve tweets"); 39 | }); 40 | return deferred.promise; 41 | }; 42 | }); 43 | -------------------------------------------------------------------------------- /frontend/app/styles/main.css: -------------------------------------------------------------------------------- 1 | .browsehappy { 2 | margin: 0.2em 0; 3 | background: #ccc; 4 | color: #000; 5 | padding: 0.2em 0; 6 | } 7 | 8 | body { 9 | padding: 0; 10 | } 11 | 12 | /* Everything but the jumbotron gets side spacing for mobile first views */ 13 | .header, 14 | .marketing, 15 | .footer { 16 | padding-left: 15px; 17 | padding-right: 15px; 18 | } 19 | 20 | /* Custom page header */ 21 | .header { 22 | /*border-bottom: 1px solid #e5e5e5;*/ 23 | margin-bottom: 10px; 24 | } 25 | /* Make the masthead heading the same height as the navigation */ 26 | .header h3 { 27 | margin-top: 0; 28 | margin-bottom: 0; 29 | line-height: 40px; 30 | padding-bottom: 19px; 31 | } 32 | 33 | /* Custom page footer */ 34 | .footer { 35 | padding-top: 19px; 36 | color: #777; 37 | /*border-top: 1px solid #e5e5e5;*/ 38 | } 39 | 40 | .container-narrow > hr { 41 | margin: 30px 0; 42 | } 43 | 44 | /* Main marketing message and sign up button */ 45 | .jumbotron { 46 | text-align: center; 47 | border-bottom: 1px solid #e5e5e5; 48 | } 49 | .jumbotron .btn { 50 | font-size: 21px; 51 | padding: 14px 24px; 52 | } 53 | 54 | /* Supporting marketing content */ 55 | .marketing { 56 | margin: 40px 0; 57 | } 58 | .marketing p + h4 { 59 | margin-top: 28px; 60 | } 61 | 62 | /* Responsive: Portrait tablets and up */ 63 | @media screen and (min-width: 768px) { 64 | .container { 65 | max-width: 1024px; 66 | } 67 | 68 | /* Remove the padding we set earlier */ 69 | .header, 70 | .marketing, 71 | .footer { 72 | padding-left: 0; 73 | padding-right: 0; 74 | } 75 | /* Space out the masthead */ 76 | .header { 77 | margin-bottom: 10px; 78 | } 79 | /* Remove the bottom border on the jumbotron for visual effect */ 80 | .jumbotron { 81 | border-bottom: 0; 82 | } 83 | } 84 | 85 | .trend{ 86 | margin-top: 2px; 87 | margin-bottom : 2px; 88 | } 89 | .verticalMargined{ 90 | margin-top: 4px; 91 | margin-bottom : 8px !important; 92 | } 93 | .labelstyle{ 94 | display: inline; 95 | font-size: 24px; 96 | margin-right: 8px; 97 | margin-bottom: 8px !important; 98 | } 99 | -------------------------------------------------------------------------------- /frontend/app/views/about.html: -------------------------------------------------------------------------------- 1 |

This is the about view.

2 | -------------------------------------------------------------------------------- /frontend/app/views/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | Trending Globally : 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | Trending Locally (India) : 14 | 15 | 16 | 17 | 18 |
19 |

20 |
21 | 22 |
23 | 24 | 26 | 27 | 28 |
29 |
30 | 31 |
32 |
33 |
34 | 35 |
36 |
37 |
38 |
39 | Sentiment : {{tweetsData[searchText].overallAnalysis.sentiment}} 40 |
41 | Number of tweets : {{tweetsData[searchText].overallAnalysis.numberOfTweets}} 42 |
43 | Number of sentimental tweets : {{tweetsData[searchText].overallAnalysis.numberOfSentimentalTweets}} 44 |
45 | Total score : {{tweetsData[searchText].overallAnalysis.totalScore}} 46 |
47 | Average score : {{tweetsData[searchText].overallAnalysis.averageScore}} 48 |
49 |
50 | 51 |

52 |
53 | 54 |
55 | 56 |
57 | Top Words : 58 |
59 | 60 | {{word.word}} {{word.appearances}} 61 | 62 |
63 |
64 | Top Positive Words : 65 |
66 | 67 | {{positiveWord.word}} {{positiveWord.appearances}} 68 | 69 |
70 |
71 | Top negative Words : 72 |
73 | 74 | {{negativeWord.word}} {{negativeWord.appearances}} 75 | 76 |
77 |
78 |
79 | -------------------------------------------------------------------------------- /frontend/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "twitter-app", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "angular": "^1.7.8", 6 | "bootstrap": "^3.4.1", 7 | "angular-animate": "^1.7.8", 8 | "angular-cookies": "^1.7.8", 9 | "angular-resource": "^1.7.8", 10 | "angular-route": "^1.7.8", 11 | "angular-sanitize": "^1.7.8", 12 | "angular-touch": "^1.7.8", 13 | "restangular": "^1.6.1", 14 | "font-awesome": "^5.11.2", 15 | "firebase": "^7.2.1", 16 | "angularfire": "^2.3.0", 17 | "angular-loading-bar": "^0.9.0", 18 | "angularjs-toaster": "^3.0.0" 19 | }, 20 | "devDependencies": { 21 | "angular-mocks": "^1.3.0" 22 | }, 23 | "appPath": "app", 24 | "moduleName": "twitterAppApp", 25 | "overrides": { 26 | "bootstrap": { 27 | "main": [ 28 | "less/bootstrap.less", 29 | "dist/css/bootstrap.css", 30 | "dist/js/bootstrap.js" 31 | ] 32 | } 33 | }, 34 | "resolutions": { 35 | "angular": "^1.7.8", 36 | "firebase": "3.x.x" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "twitterapp", 3 | "private": true, 4 | "devDependencies": { 5 | "grunt": "^0.4.5", 6 | "grunt-angular-templates": "^0.5.7", 7 | "grunt-autoprefixer": "^2.0.0", 8 | "grunt-concurrent": "^1.0.0", 9 | "grunt-contrib-clean": "^0.6.0", 10 | "grunt-contrib-concat": "^0.5.0", 11 | "grunt-contrib-connect": "^0.9.0", 12 | "grunt-contrib-copy": "^0.7.0", 13 | "grunt-contrib-cssmin": "^0.12.0", 14 | "grunt-contrib-htmlmin": "^0.4.0", 15 | "grunt-contrib-imagemin": "^0.9.2", 16 | "grunt-contrib-jshint": "^0.11.0", 17 | "grunt-contrib-uglify": "^0.7.0", 18 | "grunt-contrib-watch": "^0.6.1", 19 | "grunt-filerev": "^2.1.2", 20 | "grunt-google-cdn": "^0.4.3", 21 | "grunt-newer": "^1.1.0", 22 | "grunt-ng-annotate": "^0.9.2", 23 | "grunt-svgmin": "^2.0.0", 24 | "grunt-usemin": "^3.0.0", 25 | "grunt-wiredep": "^2.0.0", 26 | "jit-grunt": "^0.9.1", 27 | "jshint-stylish": "^1.0.0", 28 | "time-grunt": "^1.0.0" 29 | }, 30 | "engines": { 31 | "node": ">=0.10.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /frontend/test/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "browser": true, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "esnext": true, 7 | "jasmine": true, 8 | "latedef": true, 9 | "noarg": true, 10 | "node": true, 11 | "strict": true, 12 | "undef": true, 13 | "unused": true, 14 | "globals": { 15 | "angular": false, 16 | "inject": false 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /frontend/test/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.12/config/configuration-file.html 3 | // Generated on 2015-07-19 using 4 | // generator-karma 1.0.0 5 | 6 | module.exports = function(config) { 7 | 'use strict'; 8 | 9 | config.set({ 10 | // enable / disable watching file and executing tests whenever any file changes 11 | autoWatch: true, 12 | 13 | // base path, that will be used to resolve files and exclude 14 | basePath: '../', 15 | 16 | // testing framework to use (jasmine/mocha/qunit/...) 17 | // as well as any additional frameworks (requirejs/chai/sinon/...) 18 | frameworks: [ 19 | "jasmine" 20 | ], 21 | 22 | // list of files / patterns to load in the browser 23 | files: [ 24 | // bower:js 25 | 'bower_components/jquery/dist/jquery.js', 26 | 'bower_components/angular/angular.js', 27 | 'bower_components/bootstrap/dist/js/bootstrap.js', 28 | 'bower_components/angular-animate/angular-animate.js', 29 | 'bower_components/angular-cookies/angular-cookies.js', 30 | 'bower_components/angular-resource/angular-resource.js', 31 | 'bower_components/angular-route/angular-route.js', 32 | 'bower_components/angular-sanitize/angular-sanitize.js', 33 | 'bower_components/angular-touch/angular-touch.js', 34 | 'bower_components/lodash/lodash.js', 35 | 'bower_components/restangular/dist/restangular.js', 36 | 'bower_components/firebase/firebase.js', 37 | 'bower_components/angularfire/dist/angularfire.js', 38 | 'bower_components/angular-loading-bar/build/loading-bar.js', 39 | 'bower_components/angularjs-toaster/toaster.js', 40 | 'bower_components/angular-mocks/angular-mocks.js', 41 | // endbower 42 | "app/scripts/**/*.js", 43 | "test/mock/**/*.js", 44 | "test/spec/**/*.js" 45 | ], 46 | 47 | // list of files / patterns to exclude 48 | exclude: [ 49 | ], 50 | 51 | // web server port 52 | port: 8080, 53 | 54 | // Start these browsers, currently available: 55 | // - Chrome 56 | // - ChromeCanary 57 | // - Firefox 58 | // - Opera 59 | // - Safari (only Mac) 60 | // - PhantomJS 61 | // - IE (only Windows) 62 | browsers: [ 63 | "PhantomJS" 64 | ], 65 | 66 | // Which plugins to enable 67 | plugins: [ 68 | "karma-phantomjs-launcher", 69 | "karma-jasmine" 70 | ], 71 | 72 | // Continuous Integration mode 73 | // if true, it capture browsers, run tests and exit 74 | singleRun: false, 75 | 76 | colors: true, 77 | 78 | // level of logging 79 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 80 | logLevel: config.LOG_INFO, 81 | 82 | // Uncomment the following lines if you are using grunt's server to run the tests 83 | // proxies: { 84 | // '/': 'http://localhost:9000/' 85 | // }, 86 | // URL root prevent conflicts with the site root 87 | // urlRoot: '_karma_' 88 | }); 89 | }; 90 | -------------------------------------------------------------------------------- /frontend/test/spec/controllers/about.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: AboutCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('twitterAppApp')); 7 | 8 | var AboutCtrl, 9 | scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function ($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | AboutCtrl = $controller('AboutCtrl', { 15 | $scope: scope 16 | // place here mocked dependencies 17 | }); 18 | })); 19 | 20 | it('should attach a list of awesomeThings to the scope', function () { 21 | expect(AboutCtrl.awesomeThings.length).toBe(3); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /frontend/test/spec/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: MainCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('twitterAppApp')); 7 | 8 | var MainCtrl, 9 | scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function ($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | MainCtrl = $controller('MainCtrl', { 15 | $scope: scope 16 | // place here mocked dependencies 17 | }); 18 | })); 19 | 20 | it('should attach a list of awesomeThings to the scope', function () { 21 | expect(MainCtrl.awesomeThings.length).toBe(3); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /frontend/test/spec/services/server_url.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Service: SERVERURL', function () { 4 | 5 | // load the service's module 6 | beforeEach(module('twitterAppApp')); 7 | 8 | // instantiate service 9 | var SERVERURL; 10 | beforeEach(inject(function (_SERVERURL_) { 11 | SERVERURL = _SERVERURL_; 12 | })); 13 | 14 | it('should do something', function () { 15 | expect(!!SERVERURL).toBe(true); 16 | }); 17 | 18 | }); 19 | -------------------------------------------------------------------------------- /frontend/test/spec/services/twitterprovider.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Service: TwitterProvider', function () { 4 | 5 | // load the service's module 6 | beforeEach(module('twitterAppApp')); 7 | 8 | // instantiate service 9 | var TwitterProvider; 10 | beforeEach(inject(function (_TwitterProvider_) { 11 | TwitterProvider = _TwitterProvider_; 12 | })); 13 | 14 | it('should do something', function () { 15 | expect(!!TwitterProvider).toBe(true); 16 | }); 17 | 18 | }); 19 | -------------------------------------------------------------------------------- /twitter/.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /twitter/.foreverignore: -------------------------------------------------------------------------------- 1 | .tmp -------------------------------------------------------------------------------- /twitter/.gitignore: -------------------------------------------------------------------------------- 1 | ################################################ 2 | ############### .gitignore ################## 3 | ################################################ 4 | # 5 | # This file is only relevant if you are using git. 6 | # 7 | # Files which match the splat patterns below will 8 | # be ignored by git. This keeps random crap and 9 | # sensitive credentials from being uploaded to 10 | # your repository. It allows you to configure your 11 | # app for your machine without accidentally 12 | # committing settings which will smash the local 13 | # settings of other developers on your team. 14 | # 15 | # Some reasonable defaults are included below, 16 | # but, of course, you should modify/extend/prune 17 | # to fit your needs! 18 | ################################################ 19 | 20 | 21 | 22 | 23 | ################################################ 24 | # Local Configuration 25 | # 26 | # Explicitly ignore files which contain: 27 | # 28 | # 1. Sensitive information you'd rather not push to 29 | # your git repository. 30 | # e.g., your personal API keys or passwords. 31 | # 32 | # 2. Environment-specific configuration 33 | # Basically, anything that would be annoying 34 | # to have to change every time you do a 35 | # `git pull` 36 | # e.g., your local development database, or 37 | # the S3 bucket you're using for file uploads 38 | # development. 39 | # 40 | ################################################ 41 | 42 | config/local.js 43 | 44 | 45 | 46 | 47 | 48 | ################################################ 49 | # Dependencies 50 | # 51 | # When releasing a production app, you may 52 | # consider including your node_modules and 53 | # bower_components directory in your git repo, 54 | # but during development, its best to exclude it, 55 | # since different developers may be working on 56 | # different kernels, where dependencies would 57 | # need to be recompiled anyway. 58 | # 59 | # More on that here about node_modules dir: 60 | # http://www.futurealoof.com/posts/nodemodules-in-git.html 61 | # (credit Mikeal Rogers, @mikeal) 62 | # 63 | # About bower_components dir, you can see this: 64 | # http://addyosmani.com/blog/checking-in-front-end-dependencies/ 65 | # (credit Addy Osmani, @addyosmani) 66 | # 67 | ################################################ 68 | 69 | node_modules 70 | bower_components 71 | 72 | 73 | 74 | 75 | ################################################ 76 | # Sails.js / Waterline / Grunt 77 | # 78 | # Files generated by Sails and Grunt, or related 79 | # tasks and adapters. 80 | ################################################ 81 | .tmp 82 | dump.rdb 83 | 84 | 85 | 86 | 87 | 88 | ################################################ 89 | # Node.js / NPM 90 | # 91 | # Common files generated by Node, NPM, and the 92 | # related ecosystem. 93 | ################################################ 94 | lib-cov 95 | *.seed 96 | *.log 97 | *.out 98 | *.pid 99 | npm-debug.log 100 | 101 | 102 | 103 | 104 | 105 | ################################################ 106 | # Miscellaneous 107 | # 108 | # Common files generated by text editors, 109 | # operating systems, file systems, etc. 110 | ################################################ 111 | 112 | *~ 113 | *# 114 | .DS_STORE 115 | .netbeans 116 | nbproject 117 | .idea 118 | .node_history 119 | config/env/development.js -------------------------------------------------------------------------------- /twitter/.sailsrc: -------------------------------------------------------------------------------- 1 | { 2 | "generators": { 3 | "modules": {} 4 | } 5 | } -------------------------------------------------------------------------------- /twitter/README.md: -------------------------------------------------------------------------------- 1 | # twitter 2 | 3 | a [Sails](http://sailsjs.org) application 4 | -------------------------------------------------------------------------------- /twitter/api/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vishwajeetv/twitter-sentiment-analysis/2a31c537ff6e037d2e693c7c3d2cde858a7142ee/twitter/api/controllers/.gitkeep -------------------------------------------------------------------------------- /twitter/api/controllers/TweetController.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TweetController 3 | * 4 | * @description :: Server-side logic for managing tweets 5 | * @help :: See http://sailsjs.org/#!/documentation/concepts/Controllers 6 | */ 7 | 8 | var config = { 9 | "consumer_key": sails.config.consumer_key, 10 | "consumer_secret": sails.config.consumer_secret, 11 | "access_token_key" : sails.config.access_token_key, 12 | "access_token_secret" : sails.config.access_token_secret 13 | }; 14 | 15 | var firebase = require('firebase'); 16 | var app = firebase.initializeApp({ 17 | apiKey: "AIzaSyAhMcdSRJ_g7JUX3nQooeMEZhJWDoHlxpI", 18 | authDomain: "test-4073a.firebaseapp.com", 19 | databaseURL: "https://test-4073a.firebaseio.com", 20 | projectId: "test-4073a", 21 | storageBucket: "test-4073a.appspot.com", 22 | messagingSenderId: "273815567429" 23 | }); 24 | 25 | var Twitter = require('twitter'); 26 | var client = new Twitter(config); 27 | var Firebase = require("firebase"); 28 | var firebaseRef = firebase.database().ref().child("data"); 29 | 30 | 31 | module.exports = { 32 | 33 | getAnalyzedTweets: function (request , response ) 34 | { 35 | var query = encodeURIComponent(request.param('query')); 36 | console.log(query); 37 | 38 | var hasTweetsInDB = false; 39 | var tweets = Tweet.find({'query':query},function(error, tweets) { 40 | console.log(tweets.length); 41 | if(tweets.length >= 1) 42 | { 43 | hasTweetsInDB = true; 44 | getTweets(); 45 | getStream(); 46 | processTweetsFromDB() 47 | } 48 | else 49 | { 50 | getStream(); 51 | getTweets(); 52 | } 53 | }); 54 | 55 | function processTweetsFromDB() { 56 | Tweet.find({'query': query}, function (error, tweets) { 57 | if(tweets && tweets.length>0){ 58 | var sentimentalAnalysis = WordAnalysisService.countSentimentalWords(tweets); 59 | var overallAnalysis = SentimentAnalysisService.analyzeAll(tweets); 60 | var allWordAnalysis = WordAnalysisService.countAllWords(tweets); 61 | 62 | var results = { 63 | overallAnalysis: overallAnalysis, 64 | sentimentalWordsAnalysis: sentimentalAnalysis, 65 | allWordsAnalysis: allWordAnalysis 66 | }; 67 | 68 | var resultToStore = {}; 69 | resultToStore[query] = results; 70 | 71 | firebaseRef.update( 72 | resultToStore 73 | ); 74 | } 75 | 76 | 77 | }) 78 | } 79 | 80 | var getStream = function(){ 81 | 82 | client.stream('statuses/filter', {track: query}, function(stream) { 83 | var tweets = []; 84 | stream.on('data', function(tweet) { 85 | tweets.push(tweet); 86 | if(tweets.length == 2) 87 | { 88 | saveTweets(tweets); 89 | tweets = []; 90 | } 91 | }); 92 | 93 | stream.on('error', function(error) { 94 | console.log(error); 95 | }); 96 | }); 97 | 98 | var saveTweets = function(tweets){ 99 | tweets = SentimentAnalysisService.analyze(tweets); 100 | tweets.forEach(function(tweet){ 101 | tweet.query = query; 102 | Tweet.findOrCreate({id:tweet.id}, tweet).exec(function createFindCB(err, record){ 103 | 104 | }); 105 | 106 | }); 107 | processTweetsFromDB(); 108 | } 109 | }; 110 | var getTweets = function () { 111 | TwitterService.getTweets(query).then(function(tweets){ 112 | 113 | tweets = SentimentAnalysisService.analyze(tweets); 114 | tweets.forEach(function(tweet){ 115 | tweet.query = query; 116 | Tweet.findOrCreate({id:tweet.id}, tweet).exec(function createFindCB(err, record){ 117 | 118 | }); 119 | 120 | }); 121 | if(hasTweetsInDB == false) { 122 | processTweetsFromDB() 123 | return response.json({'status':'ok'}); 124 | } 125 | }); 126 | } 127 | 128 | 129 | }, 130 | 131 | getTrends : function( request , response ) 132 | { 133 | var locationId = request.param('location'); 134 | TwitterService.getTrends(locationId).then(function(trends) { 135 | return response.json({ 136 | trends: trends 137 | }); 138 | }, 139 | function(error) 140 | { 141 | return response.json({ 142 | error: error 143 | }); 144 | }) 145 | }, 146 | 147 | 148 | }; 149 | 150 | -------------------------------------------------------------------------------- /twitter/api/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vishwajeetv/twitter-sentiment-analysis/2a31c537ff6e037d2e693c7c3d2cde858a7142ee/twitter/api/models/.gitkeep -------------------------------------------------------------------------------- /twitter/api/models/Tweet.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tweet.js 3 | * 4 | * @description :: TODO: You might write a short summary of how this model works and what it represents here. 5 | * @docs :: http://sailsjs.org/#!documentation/models 6 | */ 7 | 8 | module.exports = { 9 | 10 | attributes: { 11 | id : { type: 'string'}, 12 | text: { type:'string' }, 13 | sentiment: { type: 'json' }, 14 | query: { type: 'text' } 15 | }, 16 | tableName : "tweets" 17 | }; 18 | 19 | -------------------------------------------------------------------------------- /twitter/api/policies/sessionAuth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * sessionAuth 3 | * 4 | * @module :: Policy 5 | * @description :: Simple policy to allow any authenticated user 6 | * Assumes that your login action in one of your controllers sets `req.session.authenticated = true;` 7 | * @docs :: http://sailsjs.org/#!/documentation/concepts/Policies 8 | * 9 | */ 10 | module.exports = function(req, res, next) { 11 | 12 | // User is allowed, proceed to the next policy, 13 | // or if this is the last policy, the controller 14 | if (req.session.authenticated) { 15 | return next(); 16 | } 17 | 18 | // User is not allowed 19 | // (default res.forbidden() behavior can be overridden in `config/403.js`) 20 | return res.forbidden('You are not permitted to perform this action.'); 21 | }; 22 | -------------------------------------------------------------------------------- /twitter/api/responses/badRequest.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 400 (Bad Request) Handler 3 | * 4 | * Usage: 5 | * return res.badRequest(); 6 | * return res.badRequest(data); 7 | * return res.badRequest(data, 'some/specific/badRequest/view'); 8 | * 9 | * e.g.: 10 | * ``` 11 | * return res.badRequest( 12 | * 'Please choose a valid `password` (6-12 characters)', 13 | * 'trial/signup' 14 | * ); 15 | * ``` 16 | */ 17 | 18 | module.exports = function badRequest(data, options) { 19 | 20 | // Get access to `req`, `res`, & `sails` 21 | var req = this.req; 22 | var res = this.res; 23 | var sails = req._sails; 24 | 25 | // Set status code 26 | res.status(400); 27 | 28 | // Log error to console 29 | if (data !== undefined) { 30 | sails.log.verbose('Sending 400 ("Bad Request") response: \n',data); 31 | } 32 | else sails.log.verbose('Sending 400 ("Bad Request") response'); 33 | 34 | // Only include errors in response if application environment 35 | // is not set to 'production'. In production, we shouldn't 36 | // send back any identifying information about errors. 37 | if (sails.config.environment === 'production') { 38 | data = undefined; 39 | } 40 | 41 | // If the user-agent wants JSON, always respond with JSON 42 | if (req.wantsJSON) { 43 | return res.jsonx(data); 44 | } 45 | 46 | // If second argument is a string, we take that to mean it refers to a view. 47 | // If it was omitted, use an empty object (`{}`) 48 | options = (typeof options === 'string') ? { view: options } : options || {}; 49 | 50 | // If a view was provided in options, serve it. 51 | // Otherwise try to guess an appropriate view, or if that doesn't 52 | // work, just send JSON. 53 | if (options.view) { 54 | return res.view(options.view, { data: data }); 55 | } 56 | 57 | // If no second argument provided, try to serve the implied view, 58 | // but fall back to sending JSON(P) if no view can be inferred. 59 | else return res.guessView({ data: data }, function couldNotGuessView () { 60 | return res.jsonx(data); 61 | }); 62 | 63 | }; 64 | 65 | -------------------------------------------------------------------------------- /twitter/api/responses/forbidden.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 403 (Forbidden) Handler 3 | * 4 | * Usage: 5 | * return res.forbidden(); 6 | * return res.forbidden(err); 7 | * return res.forbidden(err, 'some/specific/forbidden/view'); 8 | * 9 | * e.g.: 10 | * ``` 11 | * return res.forbidden('Access denied.'); 12 | * ``` 13 | */ 14 | 15 | module.exports = function forbidden (data, options) { 16 | 17 | // Get access to `req`, `res`, & `sails` 18 | var req = this.req; 19 | var res = this.res; 20 | var sails = req._sails; 21 | 22 | // Set status code 23 | res.status(403); 24 | 25 | // Log error to console 26 | if (data !== undefined) { 27 | sails.log.verbose('Sending 403 ("Forbidden") response: \n',data); 28 | } 29 | else sails.log.verbose('Sending 403 ("Forbidden") response'); 30 | 31 | // Only include errors in response if application environment 32 | // is not set to 'production'. In production, we shouldn't 33 | // send back any identifying information about errors. 34 | if (sails.config.environment === 'production') { 35 | data = undefined; 36 | } 37 | 38 | // If the user-agent wants JSON, always respond with JSON 39 | if (req.wantsJSON) { 40 | return res.jsonx(data); 41 | } 42 | 43 | // If second argument is a string, we take that to mean it refers to a view. 44 | // If it was omitted, use an empty object (`{}`) 45 | options = (typeof options === 'string') ? { view: options } : options || {}; 46 | 47 | // If a view was provided in options, serve it. 48 | // Otherwise try to guess an appropriate view, or if that doesn't 49 | // work, just send JSON. 50 | if (options.view) { 51 | return res.view(options.view, { data: data }); 52 | } 53 | 54 | // If no second argument provided, try to serve the default view, 55 | // but fall back to sending JSON(P) if any errors occur. 56 | else return res.view('403', { data: data }, function (err, html) { 57 | 58 | // If a view error occured, fall back to JSON(P). 59 | if (err) { 60 | // 61 | // Additionally: 62 | // • If the view was missing, ignore the error but provide a verbose log. 63 | if (err.code === 'E_VIEW_FAILED') { 64 | sails.log.verbose('res.forbidden() :: Could not locate view for error page (sending JSON instead). Details: ',err); 65 | } 66 | // Otherwise, if this was a more serious error, log to the console with the details. 67 | else { 68 | sails.log.warn('res.forbidden() :: When attempting to render error page view, an error occured (sending JSON instead). Details: ', err); 69 | } 70 | return res.jsonx(data); 71 | } 72 | 73 | return res.send(html); 74 | }); 75 | 76 | }; 77 | 78 | -------------------------------------------------------------------------------- /twitter/api/responses/notFound.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 404 (Not Found) Handler 3 | * 4 | * Usage: 5 | * return res.notFound(); 6 | * return res.notFound(err); 7 | * return res.notFound(err, 'some/specific/notfound/view'); 8 | * 9 | * e.g.: 10 | * ``` 11 | * return res.notFound(); 12 | * ``` 13 | * 14 | * NOTE: 15 | * If a request doesn't match any explicit routes (i.e. `config/routes.js`) 16 | * or route blueprints (i.e. "shadow routes", Sails will call `res.notFound()` 17 | * automatically. 18 | */ 19 | 20 | module.exports = function notFound (data, options) { 21 | 22 | // Get access to `req`, `res`, & `sails` 23 | var req = this.req; 24 | var res = this.res; 25 | var sails = req._sails; 26 | 27 | // Set status code 28 | res.status(404); 29 | 30 | // Log error to console 31 | if (data !== undefined) { 32 | sails.log.verbose('Sending 404 ("Not Found") response: \n',data); 33 | } 34 | else sails.log.verbose('Sending 404 ("Not Found") response'); 35 | 36 | // Only include errors in response if application environment 37 | // is not set to 'production'. In production, we shouldn't 38 | // send back any identifying information about errors. 39 | if (sails.config.environment === 'production') { 40 | data = undefined; 41 | } 42 | 43 | // If the user-agent wants JSON, always respond with JSON 44 | if (req.wantsJSON) { 45 | return res.jsonx(data); 46 | } 47 | 48 | // If second argument is a string, we take that to mean it refers to a view. 49 | // If it was omitted, use an empty object (`{}`) 50 | options = (typeof options === 'string') ? { view: options } : options || {}; 51 | 52 | // If a view was provided in options, serve it. 53 | // Otherwise try to guess an appropriate view, or if that doesn't 54 | // work, just send JSON. 55 | if (options.view) { 56 | return res.view(options.view, { data: data }); 57 | } 58 | 59 | // If no second argument provided, try to serve the default view, 60 | // but fall back to sending JSON(P) if any errors occur. 61 | else return res.view('404', { data: data }, function (err, html) { 62 | 63 | // If a view error occured, fall back to JSON(P). 64 | if (err) { 65 | // 66 | // Additionally: 67 | // • If the view was missing, ignore the error but provide a verbose log. 68 | if (err.code === 'E_VIEW_FAILED') { 69 | sails.log.verbose('res.notFound() :: Could not locate view for error page (sending JSON instead). Details: ',err); 70 | } 71 | // Otherwise, if this was a more serious error, log to the console with the details. 72 | else { 73 | sails.log.warn('res.notFound() :: When attempting to render error page view, an error occured (sending JSON instead). Details: ', err); 74 | } 75 | return res.jsonx(data); 76 | } 77 | 78 | return res.send(html); 79 | }); 80 | 81 | }; 82 | 83 | -------------------------------------------------------------------------------- /twitter/api/responses/ok.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 200 (OK) Response 3 | * 4 | * Usage: 5 | * return res.ok(); 6 | * return res.ok(data); 7 | * return res.ok(data, 'auth/login'); 8 | * 9 | * @param {Object} data 10 | * @param {String|Object} options 11 | * - pass string to render specified view 12 | */ 13 | 14 | module.exports = function sendOK (data, options) { 15 | 16 | // Get access to `req`, `res`, & `sails` 17 | var req = this.req; 18 | var res = this.res; 19 | var sails = req._sails; 20 | 21 | sails.log.silly('res.ok() :: Sending 200 ("OK") response'); 22 | 23 | // Set status code 24 | res.status(200); 25 | 26 | // If appropriate, serve data as JSON(P) 27 | if (req.wantsJSON) { 28 | return res.jsonx(data); 29 | } 30 | 31 | // If second argument is a string, we take that to mean it refers to a view. 32 | // If it was omitted, use an empty object (`{}`) 33 | options = (typeof options === 'string') ? { view: options } : options || {}; 34 | 35 | // If a view was provided in options, serve it. 36 | // Otherwise try to guess an appropriate view, or if that doesn't 37 | // work, just send JSON. 38 | if (options.view) { 39 | return res.view(options.view, { data: data }); 40 | } 41 | 42 | // If no second argument provided, try to serve the implied view, 43 | // but fall back to sending JSON(P) if no view can be inferred. 44 | else return res.guessView({ data: data }, function couldNotGuessView () { 45 | return res.jsonx(data); 46 | }); 47 | 48 | }; 49 | -------------------------------------------------------------------------------- /twitter/api/responses/serverError.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 500 (Server Error) Response 3 | * 4 | * Usage: 5 | * return res.serverError(); 6 | * return res.serverError(err); 7 | * return res.serverError(err, 'some/specific/error/view'); 8 | * 9 | * NOTE: 10 | * If something throws in a policy or controller, or an internal 11 | * error is encountered, Sails will call `res.serverError()` 12 | * automatically. 13 | */ 14 | 15 | module.exports = function serverError (data, options) { 16 | 17 | // Get access to `req`, `res`, & `sails` 18 | var req = this.req; 19 | var res = this.res; 20 | var sails = req._sails; 21 | 22 | // Set status code 23 | res.status(500); 24 | 25 | // Log error to console 26 | if (data !== undefined) { 27 | sails.log.error('Sending 500 ("Server Error") response: \n',data); 28 | } 29 | else sails.log.error('Sending empty 500 ("Server Error") response'); 30 | 31 | // Only include errors in response if application environment 32 | // is not set to 'production'. In production, we shouldn't 33 | // send back any identifying information about errors. 34 | if (sails.config.environment === 'production') { 35 | data = undefined; 36 | } 37 | 38 | // If the user-agent wants JSON, always respond with JSON 39 | if (req.wantsJSON) { 40 | return res.jsonx(data); 41 | } 42 | 43 | // If second argument is a string, we take that to mean it refers to a view. 44 | // If it was omitted, use an empty object (`{}`) 45 | options = (typeof options === 'string') ? { view: options } : options || {}; 46 | 47 | // If a view was provided in options, serve it. 48 | // Otherwise try to guess an appropriate view, or if that doesn't 49 | // work, just send JSON. 50 | if (options.view) { 51 | return res.view(options.view, { data: data }); 52 | } 53 | 54 | // If no second argument provided, try to serve the default view, 55 | // but fall back to sending JSON(P) if any errors occur. 56 | else return res.view('500', { data: data }, function (err, html) { 57 | 58 | // If a view error occured, fall back to JSON(P). 59 | if (err) { 60 | // 61 | // Additionally: 62 | // • If the view was missing, ignore the error but provide a verbose log. 63 | if (err.code === 'E_VIEW_FAILED') { 64 | sails.log.verbose('res.serverError() :: Could not locate view for error page (sending JSON instead). Details: ',err); 65 | } 66 | // Otherwise, if this was a more serious error, log to the console with the details. 67 | else { 68 | sails.log.warn('res.serverError() :: When attempting to render error page view, an error occured (sending JSON instead). Details: ', err); 69 | } 70 | return res.jsonx(data); 71 | } 72 | 73 | return res.send(html); 74 | }); 75 | 76 | }; 77 | 78 | -------------------------------------------------------------------------------- /twitter/api/services/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vishwajeetv/twitter-sentiment-analysis/2a31c537ff6e037d2e693c7c3d2cde858a7142ee/twitter/api/services/.gitkeep -------------------------------------------------------------------------------- /twitter/api/services/SentimentAnalysisService.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by vishwajeetv on 25/07/15. 3 | */ 4 | 5 | 6 | function calculateSentiment(averageScore) 7 | { 8 | var sentiment = null; 9 | var minimumSentimentIndex = 0.5; 10 | var highSentimentIndex = 2; 11 | if(averageScore > minimumSentimentIndex){ 12 | sentiment = 'Positive'; 13 | if(averageScore > highSentimentIndex) 14 | { 15 | sentiment = 'Very Positive'; 16 | } 17 | } 18 | else if( averageScore < -minimumSentimentIndex){ 19 | sentiment = 'Negative'; 20 | if(averageScore < -highSentimentIndex) 21 | { 22 | sentiment = 'Very Negative'; 23 | } 24 | } 25 | else 26 | { 27 | sentiment = 'Neutral'; 28 | } 29 | return sentiment; 30 | } 31 | 32 | var Sentiment = require('sentiment'); 33 | var sentiment = new Sentiment(); 34 | 35 | module.exports = { 36 | 37 | 38 | analyze: function(tweets) { 39 | 40 | var favouriteScoreModifier = 10; //number by which favourite count should be divided. 41 | tweets.forEach(function(tweet) 42 | { 43 | if(tweet && tweet.text){ 44 | tweet.sentiment = sentiment.analyze(tweet.text); 45 | 46 | if(tweet.sentiment && tweet.favorite_count && tweet.favorite_count > 0) 47 | { 48 | tweet.sentiment.score = (tweet.sentiment.score + 49 | ((tweet.sentiment.score * tweet.favorite_count)/favouriteScoreModifier)); 50 | } 51 | } 52 | 53 | }); 54 | return tweets; 55 | 56 | }, 57 | 58 | analyzeAll: function(tweets){ 59 | var totalScore = 0; 60 | var averageScore = 0; 61 | var numberOfTweets = tweets.length; 62 | var numberOfSentimentalTweets = 0; 63 | tweets.forEach(function(tweet) 64 | { 65 | if(tweet && tweet.sentiment && tweet.sentiment.score != 0) //skiping neutral tweets 66 | { 67 | numberOfSentimentalTweets++; 68 | totalScore = parseInt(totalScore + tweet.sentiment.score); 69 | } 70 | }); 71 | 72 | if(numberOfSentimentalTweets != 0){ 73 | averageScore = parseFloat(totalScore/numberOfSentimentalTweets); 74 | } 75 | 76 | var analysis = { 77 | numberOfTweets : numberOfTweets, 78 | numberOfSentimentalTweets: numberOfSentimentalTweets, 79 | averageScore : averageScore, 80 | totalScore : totalScore, 81 | sentiment : calculateSentiment(averageScore) 82 | }; 83 | 84 | return analysis; 85 | } 86 | 87 | 88 | }; 89 | -------------------------------------------------------------------------------- /twitter/api/services/TwitterService.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by vishwajeetv on 25/07/15. 3 | */ 4 | 5 | 6 | var config = { 7 | "consumer_key": sails.config.consumer_key, 8 | "consumer_secret": sails.config.consumer_secret, 9 | "access_token_key" : sails.config.access_token_key, 10 | "access_token_secret" : sails.config.access_token_secret 11 | }; 12 | 13 | var Twitter = require('twitter'); 14 | var client = new Twitter(config); 15 | 16 | var Promise = require('bluebird'); 17 | 18 | module.exports = { 19 | 20 | 21 | getTweets: function(query) { 22 | 23 | //var params = { q: '#indvszim', count: 100}; 24 | var params = { q: query, count: 100}; 25 | 26 | var tweets; 27 | 28 | return new Promise( function( resolve, reject ) 29 | { 30 | client.get('search/tweets/', params, function(error, tweets, response){ 31 | if (!error) { 32 | var statuses = tweets.statuses; 33 | return resolve(statuses); 34 | } 35 | else{ 36 | console.log(error); 37 | } 38 | }) 39 | }); 40 | }, 41 | 42 | getTrends : function(locationId){ 43 | return new Promise( function( resolve, reject ) 44 | { 45 | var params = { id: locationId }; 46 | client.get('trends/place/', params, function(error, trends, response){ 47 | if (!error) { 48 | return resolve(trends); 49 | } 50 | else{ 51 | console.log(error); 52 | return reject(error); 53 | } 54 | }) 55 | }); 56 | 57 | } 58 | 59 | 60 | }; -------------------------------------------------------------------------------- /twitter/api/services/WordAnalysisService.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by vishwajeetv on 25/07/15. 3 | */ 4 | 5 | function analyzeCount(words) { 6 | 7 | var natural = require('natural'); 8 | if (words.length == 0) { 9 | return null; 10 | } 11 | var wordAnalysis = {}; 12 | words.forEach(function (word) { 13 | var hasMatch = false; 14 | 15 | function checkMatch(element, index, words) { 16 | return natural.JaroWinklerDistance(word,element) > 0.8; 17 | } 18 | 19 | hasMatch = words.some(checkMatch); 20 | 21 | if (hasMatch) { 22 | if (wordAnalysis[word]) { 23 | wordAnalysis[word] = parseInt(wordAnalysis[word] + 1); 24 | } 25 | else { 26 | wordAnalysis[word] = 1; 27 | } 28 | 29 | } 30 | }); 31 | var results = []; 32 | for (var key in wordAnalysis) { 33 | if (wordAnalysis.hasOwnProperty(key)) { 34 | results.push( 35 | { 36 | word: key, appearances: wordAnalysis[key] 37 | } 38 | ) 39 | } 40 | } 41 | wordAnalysis = results.sort( 42 | function (a, b) { 43 | if (a.appearances > b.appearances) { 44 | return -1; 45 | } 46 | if (a.appearances < b.appearances) { 47 | return 1; 48 | } 49 | // a must be equal to b 50 | return 0; 51 | } 52 | ); 53 | 54 | return wordAnalysis; 55 | } 56 | 57 | module.exports = { 58 | 59 | countSentimentalWords: function(tweets) { 60 | 61 | var positiveWords = []; 62 | var negativeWords = []; 63 | var allWordAnalysis = {}; 64 | tweets.forEach(function(tweet){ 65 | 66 | if(tweet && tweet.sentiment){ 67 | if(tweet.sentiment.positive.length >= 1) { 68 | tweet.sentiment.positive.forEach(function (positiveWord) { 69 | positiveWords.push(positiveWord); 70 | }) 71 | 72 | } 73 | if(tweet.sentiment. negative.length >= 1) { 74 | 75 | tweet.sentiment.negative.forEach(function (negativeWord) { 76 | negativeWords.push(negativeWord); 77 | }) 78 | } 79 | } 80 | 81 | }); 82 | allWordAnalysis.positive = analyzeCount(positiveWords); 83 | allWordAnalysis.negative = analyzeCount(negativeWords); 84 | 85 | return allWordAnalysis; 86 | 87 | 88 | 89 | }, 90 | 91 | countAllWords : function(tweets){ 92 | 93 | var pos = require('pos'); 94 | 95 | 96 | var allWords = []; 97 | tweets.forEach(function(tweet){ 98 | 99 | if((typeof (tweet.text) !== undefined)) { 100 | 101 | 102 | var words = new pos.Lexer().lex(tweet.text); 103 | var taggedWords = new pos.Tagger().tag(words); 104 | 105 | taggedWords.forEach(function (word) { 106 | 107 | if (word[1] == ( "NNP" || "NNPS" || "NNS" )) { 108 | if (word[0].length > 2) { 109 | allWords.push(word[0]); 110 | } 111 | } 112 | }); 113 | } 114 | }); 115 | 116 | var allWordsAnalysis = analyzeCount(allWords) 117 | 118 | return allWordsAnalysis; 119 | } 120 | 121 | }; 122 | -------------------------------------------------------------------------------- /twitter/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * app.js 3 | * 4 | * Use `app.js` to run your app without `sails lift`. 5 | * To start the server, run: `node app.js`. 6 | * 7 | * This is handy in situations where the sails CLI is not relevant or useful. 8 | * 9 | * For example: 10 | * => `node app.js` 11 | * => `forever start app.js` 12 | * => `node debug app.js` 13 | * => `modulus deploy` 14 | * => `heroku scale` 15 | * 16 | * 17 | * The same command-line arguments are supported, e.g.: 18 | * `node app.js --silent --port=80 --prod` 19 | */ 20 | 21 | // Ensure we're in the project directory, so relative paths work as expected 22 | // no matter where we actually lift from. 23 | process.chdir(__dirname); 24 | 25 | // Ensure a "sails" can be located: 26 | (function() { 27 | var sails; 28 | try { 29 | sails = require('sails'); 30 | } catch (e) { 31 | console.error('To run an app using `node app.js`, you usually need to have a version of `sails` installed in the same directory as your app.'); 32 | console.error('To do that, run `npm install sails`'); 33 | console.error(''); 34 | console.error('Alternatively, if you have sails installed globally (i.e. you did `npm install -g sails`), you can use `sails lift`.'); 35 | console.error('When you run `sails lift`, your app will still use a local `./node_modules/sails` dependency if it exists,'); 36 | console.error('but if it doesn\'t, the app will run with the global sails instead!'); 37 | return; 38 | } 39 | 40 | // Try to get `rc` dependency 41 | var rc; 42 | try { 43 | rc = require('rc'); 44 | } catch (e0) { 45 | try { 46 | rc = require('sails/node_modules/rc'); 47 | } catch (e1) { 48 | console.error('Could not find dependency: `rc`.'); 49 | console.error('Your `.sailsrc` file(s) will be ignored.'); 50 | console.error('To resolve this, run:'); 51 | console.error('npm install rc --save'); 52 | rc = function () { return {}; }; 53 | } 54 | } 55 | 56 | 57 | // Start server 58 | sails.lift(rc('sails')); 59 | })(); 60 | -------------------------------------------------------------------------------- /twitter/config/blueprints.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Blueprint API Configuration 3 | * (sails.config.blueprints) 4 | * 5 | * These settings are for the global configuration of blueprint routes and 6 | * request options (which impact the behavior of blueprint actions). 7 | * 8 | * You may also override any of these settings on a per-controller basis 9 | * by defining a '_config' key in your controller defintion, and assigning it 10 | * a configuration object with overrides for the settings in this file. 11 | * A lot of the configuration options below affect so-called "CRUD methods", 12 | * or your controllers' `find`, `create`, `update`, and `destroy` actions. 13 | * 14 | * It's important to realize that, even if you haven't defined these yourself, as long as 15 | * a model exists with the same name as the controller, Sails will respond with built-in CRUD 16 | * logic in the form of a JSON API, including support for sort, pagination, and filtering. 17 | * 18 | * For more information on the blueprint API, check out: 19 | * http://sailsjs.org/#!/documentation/reference/blueprint-api 20 | * 21 | * For more information on the settings in this file, see: 22 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.blueprints.html 23 | * 24 | */ 25 | 26 | module.exports.blueprints = { 27 | 28 | /*************************************************************************** 29 | * * 30 | * Action routes speed up the backend development workflow by * 31 | * eliminating the need to manually bind routes. When enabled, GET, POST, * 32 | * PUT, and DELETE routes will be generated for every one of a controller's * 33 | * actions. * 34 | * * 35 | * If an `index` action exists, additional naked routes will be created for * 36 | * it. Finally, all `actions` blueprints support an optional path * 37 | * parameter, `id`, for convenience. * 38 | * * 39 | * `actions` are enabled by default, and can be OK for production-- * 40 | * however, if you'd like to continue to use controller/action autorouting * 41 | * in a production deployment, you must take great care not to * 42 | * inadvertently expose unsafe/unintentional controller logic to GET * 43 | * requests. * 44 | * * 45 | ***************************************************************************/ 46 | 47 | // actions: true, 48 | 49 | /*************************************************************************** 50 | * * 51 | * RESTful routes (`sails.config.blueprints.rest`) * 52 | * * 53 | * REST blueprints are the automatically generated routes Sails uses to * 54 | * expose a conventional REST API on top of a controller's `find`, * 55 | * `create`, `update`, and `destroy` actions. * 56 | * * 57 | * For example, a BoatController with `rest` enabled generates the * 58 | * following routes: * 59 | * ::::::::::::::::::::::::::::::::::::::::::::::::::::::: * 60 | * GET /boat -> BoatController.find * 61 | * GET /boat/:id -> BoatController.findOne * 62 | * POST /boat -> BoatController.create * 63 | * PUT /boat/:id -> BoatController.update * 64 | * DELETE /boat/:id -> BoatController.destroy * 65 | * * 66 | * `rest` blueprint routes are enabled by default, and are suitable for use * 67 | * in a production scenario, as long you take standard security precautions * 68 | * (combine w/ policies, etc.) * 69 | * * 70 | ***************************************************************************/ 71 | 72 | // rest: true, 73 | 74 | /*************************************************************************** 75 | * * 76 | * Shortcut routes are simple helpers to provide access to a * 77 | * controller's CRUD methods from your browser's URL bar. When enabled, * 78 | * GET, POST, PUT, and DELETE routes will be generated for the * 79 | * controller's`find`, `create`, `update`, and `destroy` actions. * 80 | * * 81 | * `shortcuts` are enabled by default, but should be disabled in * 82 | * production. * 83 | * * 84 | ***************************************************************************/ 85 | 86 | // shortcuts: true, 87 | 88 | /*************************************************************************** 89 | * * 90 | * An optional mount path for all blueprint routes on a controller, * 91 | * including `rest`, `actions`, and `shortcuts`. This allows you to take * 92 | * advantage of blueprint routing, even if you need to namespace your API * 93 | * methods. * 94 | * * 95 | * (NOTE: This only applies to blueprint autoroutes, not manual routes from * 96 | * `sails.config.routes`) * 97 | * * 98 | ***************************************************************************/ 99 | 100 | // prefix: '', 101 | 102 | /*************************************************************************** 103 | * * 104 | * An optional mount path for all REST blueprint routes on a controller. * 105 | * And it do not include `actions` and `shortcuts` routes. * 106 | * This allows you to take advantage of REST blueprint routing, * 107 | * even if you need to namespace your RESTful API methods * 108 | * * 109 | ***************************************************************************/ 110 | 111 | // restPrefix: '', 112 | 113 | /*************************************************************************** 114 | * * 115 | * Whether to pluralize controller names in blueprint routes. * 116 | * * 117 | * (NOTE: This only applies to blueprint autoroutes, not manual routes from * 118 | * `sails.config.routes`) * 119 | * * 120 | * For example, REST blueprints for `FooController` with `pluralize` * 121 | * enabled: * 122 | * GET /foos/:id? * 123 | * POST /foos * 124 | * PUT /foos/:id? * 125 | * DELETE /foos/:id? * 126 | * * 127 | ***************************************************************************/ 128 | 129 | // pluralize: false, 130 | 131 | /*************************************************************************** 132 | * * 133 | * Whether the blueprint controllers should populate model fetches with * 134 | * data from other models which are linked by associations * 135 | * * 136 | * If you have a lot of data in one-to-many associations, leaving this on * 137 | * may result in very heavy api calls * 138 | * * 139 | ***************************************************************************/ 140 | 141 | // populate: true, 142 | 143 | /**************************************************************************** 144 | * * 145 | * Whether to run Model.watch() in the find and findOne blueprint actions. * 146 | * Can be overridden on a per-model basis. * 147 | * * 148 | ****************************************************************************/ 149 | 150 | // autoWatch: true, 151 | 152 | /**************************************************************************** 153 | * * 154 | * The default number of records to show in the response from a "find" * 155 | * action. Doubles as the default size of populated arrays if populate is * 156 | * true. * 157 | * * 158 | ****************************************************************************/ 159 | 160 | defaultLimit: 1000 161 | 162 | }; 163 | -------------------------------------------------------------------------------- /twitter/config/bootstrap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bootstrap 3 | * (sails.config.bootstrap) 4 | * 5 | * An asynchronous bootstrap function that runs before your Sails app gets lifted. 6 | * This gives you an opportunity to set up your data model, run jobs, or perform some special logic. 7 | * 8 | * For more information on bootstrapping your app, check out: 9 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.bootstrap.html 10 | */ 11 | 12 | module.exports.bootstrap = function(cb) { 13 | 14 | // It's very important to trigger this callback method when you are finished 15 | // with the bootstrap! (otherwise your server will never lift, since it's waiting on the bootstrap) 16 | cb(); 17 | }; 18 | -------------------------------------------------------------------------------- /twitter/config/connections.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Connections 3 | * (sails.config.connections) 4 | * 5 | * `Connections` are like "saved settings" for your adapters. What's the difference between 6 | * a connection and an adapter, you might ask? An adapter (e.g. `sails-mysql`) is generic-- 7 | * it needs some additional information to work (e.g. your database host, password, user, etc.) 8 | * A `connection` is that additional information. 9 | * 10 | * Each model must have a `connection` property (a string) which is references the name of one 11 | * of these connections. If it doesn't, the default `connection` configured in `config/models.js` 12 | * will be applied. Of course, a connection can (and usually is) shared by multiple models. 13 | * . 14 | * Note: If you're using version control, you should put your passwords/api keys 15 | * in `config/local.js`, environment variables, or use another strategy. 16 | * (this is to prevent you inadvertently sensitive credentials up to your repository.) 17 | * 18 | * For more information on configuration, check out: 19 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.connections.html 20 | */ 21 | 22 | module.exports.connections = { 23 | 24 | /*************************************************************************** 25 | * * 26 | * Local disk storage for DEVELOPMENT ONLY * 27 | * * 28 | * Installed by default. * 29 | * * 30 | ***************************************************************************/ 31 | localDiskDb: { 32 | adapter: 'sails-disk' 33 | }, 34 | 35 | /*************************************************************************** 36 | * * 37 | * MySQL is the world's most popular relational database. * 38 | * http://en.wikipedia.org/wiki/MySQL * 39 | * * 40 | * Run: npm install sails-mysql * 41 | * * 42 | ***************************************************************************/ 43 | someMysqlServer: { 44 | adapter: 'sails-mysql', 45 | host: 'YOUR_MYSQL_SERVER_HOSTNAME_OR_IP_ADDRESS', 46 | user: 'YOUR_MYSQL_USER', 47 | password: 'YOUR_MYSQL_PASSWORD', 48 | database: 'YOUR_MYSQL_DB' 49 | }, 50 | 51 | /*************************************************************************** 52 | * * 53 | * MongoDB is the leading NoSQL database. * 54 | * http://en.wikipedia.org/wiki/MongoDB * 55 | * * 56 | * Run: npm install sails-mongo * 57 | * * 58 | ***************************************************************************/ 59 | someMongodbServer: { 60 | adapter: 'sails-mongo', 61 | host: 'localhost', 62 | port: 27017, 63 | // user: 'username', 64 | // password: 'password', 65 | database: 'twitter' 66 | }, 67 | 68 | /*************************************************************************** 69 | * * 70 | * PostgreSQL is another officially supported relational database. * 71 | * http://en.wikipedia.org/wiki/PostgreSQL * 72 | * * 73 | * Run: npm install sails-postgresql * 74 | * * 75 | * * 76 | ***************************************************************************/ 77 | somePostgresqlServer: { 78 | adapter: 'sails-postgresql', 79 | host: 'YOUR_POSTGRES_SERVER_HOSTNAME_OR_IP_ADDRESS', 80 | user: 'YOUR_POSTGRES_USER', 81 | password: 'YOUR_POSTGRES_PASSWORD', 82 | database: 'YOUR_POSTGRES_DB' 83 | } 84 | 85 | 86 | /*************************************************************************** 87 | * * 88 | * More adapters: https://github.com/balderdashy/sails * 89 | * * 90 | ***************************************************************************/ 91 | 92 | }; 93 | -------------------------------------------------------------------------------- /twitter/config/cors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Cross-Origin Resource Sharing (CORS) Settings 3 | * (sails.config.cors) 4 | * 5 | * CORS is like a more modern version of JSONP-- it allows your server/API 6 | * to successfully respond to requests from client-side JavaScript code 7 | * running on some other domain (e.g. google.com) 8 | * Unlike JSONP, it works with POST, PUT, and DELETE requests 9 | * 10 | * For more information on CORS, check out: 11 | * http://en.wikipedia.org/wiki/Cross-origin_resource_sharing 12 | * 13 | * Note that any of these settings (besides 'allRoutes') can be changed on a per-route basis 14 | * by adding a "cors" object to the route configuration: 15 | * 16 | * '/get foo': { 17 | * controller: 'foo', 18 | * action: 'bar', 19 | * cors: { 20 | * origin: 'http://foobar.com,https://owlhoot.com' 21 | * } 22 | * } 23 | * 24 | * For more information on this configuration file, see: 25 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.cors.html 26 | * 27 | */ 28 | 29 | module.exports.cors = { 30 | 31 | /*************************************************************************** 32 | * * 33 | * Allow CORS on all routes by default? If not, you must enable CORS on a * 34 | * per-route basis by either adding a "cors" configuration object to the * 35 | * route config, or setting "cors:true" in the route config to use the * 36 | * default settings below. * 37 | * * 38 | ***************************************************************************/ 39 | 40 | allRoutes: true, 41 | 42 | /*************************************************************************** 43 | * * 44 | * Which domains which are allowed CORS access? This can be a * 45 | * comma-delimited list of hosts (beginning with http:// or https://) or * 46 | * "*" to allow all domains CORS access. * 47 | * * 48 | ***************************************************************************/ 49 | 50 | origin: '*', 51 | 52 | /*************************************************************************** 53 | * * 54 | * Allow cookies to be shared for CORS requests? * 55 | * * 56 | ***************************************************************************/ 57 | 58 | // credentials: true, 59 | 60 | /*************************************************************************** 61 | * * 62 | * Which methods should be allowed for CORS requests? This is only used in * 63 | * response to preflight requests (see article linked above for more info) * 64 | * * 65 | ***************************************************************************/ 66 | 67 | methods: 'GET, POST, PUT, DELETE, OPTIONS, HEAD', 68 | 69 | /*************************************************************************** 70 | * * 71 | * Which headers should be allowed for CORS requests? This is only used in * 72 | * response to preflight requests. * 73 | * * 74 | ***************************************************************************/ 75 | 76 | // headers: 'content-type' 77 | 78 | }; 79 | -------------------------------------------------------------------------------- /twitter/config/csrf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Cross-Site Request Forgery Protection Settings 3 | * (sails.config.csrf) 4 | * 5 | * CSRF tokens are like a tracking chip. While a session tells the server that a user 6 | * "is who they say they are", a csrf token tells the server "you are where you say you are". 7 | * 8 | * When enabled, all non-GET requests to the Sails server must be accompanied by 9 | * a special token, identified as the '_csrf' parameter. 10 | * 11 | * This option protects your Sails app against cross-site request forgery (or CSRF) attacks. 12 | * A would-be attacker needs not only a user's session cookie, but also this timestamped, 13 | * secret CSRF token, which is refreshed/granted when the user visits a URL on your app's domain. 14 | * 15 | * This allows us to have certainty that our users' requests haven't been hijacked, 16 | * and that the requests they're making are intentional and legitimate. 17 | * 18 | * This token has a short-lived expiration timeline, and must be acquired by either: 19 | * 20 | * (a) For traditional view-driven web apps: 21 | * Fetching it from one of your views, where it may be accessed as 22 | * a local variable, e.g.: 23 | *
24 | * 25 | *
26 | * 27 | * or (b) For AJAX/Socket-heavy and/or single-page apps: 28 | * Sending a GET request to the `/csrfToken` route, where it will be returned 29 | * as JSON, e.g.: 30 | * { _csrf: 'ajg4JD(JGdajhLJALHDa' } 31 | * 32 | * 33 | * Enabling this option requires managing the token in your front-end app. 34 | * For traditional web apps, it's as easy as passing the data from a view into a form action. 35 | * In AJAX/Socket-heavy apps, just send a GET request to the /csrfToken route to get a valid token. 36 | * 37 | * For more information on CSRF, check out: 38 | * http://en.wikipedia.org/wiki/Cross-site_request_forgery 39 | * 40 | * For more information on this configuration file, including info on CSRF + CORS, see: 41 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.csrf.html 42 | * 43 | */ 44 | 45 | /**************************************************************************** 46 | * * 47 | * Enabled CSRF protection for your site? * 48 | * * 49 | ****************************************************************************/ 50 | 51 | // module.exports.csrf = false; 52 | 53 | /**************************************************************************** 54 | * * 55 | * You may also specify more fine-grained settings for CSRF, including the * 56 | * domains which are allowed to request the CSRF token via AJAX. These * 57 | * settings override the general CORS settings in your config/cors.js file. * 58 | * * 59 | ****************************************************************************/ 60 | 61 | // module.exports.csrf = { 62 | // grantTokenViaAjax: true, 63 | // origin: '' 64 | // } 65 | -------------------------------------------------------------------------------- /twitter/config/env/development.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Development environment settings 3 | * 4 | * This file can include shared settings for a development team, 5 | * such as API keys or remote database passwords. If you're using 6 | * a version control solution for your Sails app, this file will 7 | * be committed to your repository unless you add it to your .gitignore 8 | * file. If your repository will be publicly viewable, don't add 9 | * any private information to this file! 10 | * 11 | */ 12 | 13 | module.exports = { 14 | 15 | /*************************************************************************** 16 | * Set the default database connection for models in the development * 17 | * environment (see config/connections.js and config/models.js ) * 18 | ***************************************************************************/ 19 | 20 | // models: { 21 | // connection: 'someMongodbServer' 22 | // } 23 | 24 | "consumer_key": "", 25 | "consumer_secret": "", 26 | "access_token_key" : "", 27 | "access_token_secret" : "" 28 | 29 | 30 | }; 31 | -------------------------------------------------------------------------------- /twitter/config/env/production.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Production environment settings 3 | * 4 | * This file can include shared settings for a production environment, 5 | * such as API keys or remote database passwords. If you're using 6 | * a version control solution for your Sails app, this file will 7 | * be committed to your repository unless you add it to your .gitignore 8 | * file. If your repository will be publicly viewable, don't add 9 | * any private information to this file! 10 | * 11 | */ 12 | 13 | module.exports = { 14 | 15 | /*************************************************************************** 16 | * Set the default database connection for models in the production * 17 | * environment (see config/connections.js and config/models.js ) * 18 | ***************************************************************************/ 19 | 20 | // models: { 21 | // connection: 'someMysqlServer' 22 | // }, 23 | 24 | /*************************************************************************** 25 | * Set the port in the production environment to 80 * 26 | ***************************************************************************/ 27 | 28 | // port: 80, 29 | 30 | /*************************************************************************** 31 | * Set the log level in production environment to "silent" * 32 | ***************************************************************************/ 33 | 34 | // log: { 35 | // level: "silent" 36 | // } 37 | 38 | }; 39 | -------------------------------------------------------------------------------- /twitter/config/globals.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Global Variable Configuration 3 | * (sails.config.globals) 4 | * 5 | * Configure which global variables which will be exposed 6 | * automatically by Sails. 7 | * 8 | * For more information on configuration, check out: 9 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.globals.html 10 | */ 11 | module.exports.globals = { 12 | 13 | /**************************************************************************** 14 | * * 15 | * Expose the lodash installed in Sails core as a global variable. If this * 16 | * is disabled, like any other node module you can always run npm install * 17 | * lodash --save, then var _ = require('lodash') at the top of any file. * 18 | * * 19 | ****************************************************************************/ 20 | 21 | // _: true, 22 | 23 | /**************************************************************************** 24 | * * 25 | * Expose the async installed in Sails core as a global variable. If this is * 26 | * disabled, like any other node module you can always run npm install async * 27 | * --save, then var async = require('async') at the top of any file. * 28 | * * 29 | ****************************************************************************/ 30 | 31 | // async: true, 32 | 33 | /**************************************************************************** 34 | * * 35 | * Expose the sails instance representing your app. If this is disabled, you * 36 | * can still get access via req._sails. * 37 | * * 38 | ****************************************************************************/ 39 | 40 | // sails: true, 41 | 42 | /**************************************************************************** 43 | * * 44 | * Expose each of your app's services as global variables (using their * 45 | * "globalId"). E.g. a service defined in api/models/NaturalLanguage.js * 46 | * would have a globalId of NaturalLanguage by default. If this is disabled, * 47 | * you can still access your services via sails.services.* * 48 | * * 49 | ****************************************************************************/ 50 | 51 | // services: true, 52 | 53 | /**************************************************************************** 54 | * * 55 | * Expose each of your app's models as global variables (using their * 56 | * "globalId"). E.g. a model defined in api/models/User.js would have a * 57 | * globalId of User by default. If this is disabled, you can still access * 58 | * your models via sails.models.*. * 59 | * * 60 | ****************************************************************************/ 61 | 62 | // models: true 63 | }; 64 | -------------------------------------------------------------------------------- /twitter/config/http.js: -------------------------------------------------------------------------------- 1 | /** 2 | * HTTP Server Settings 3 | * (sails.config.http) 4 | * 5 | * Configuration for the underlying HTTP server in Sails. 6 | * Only applies to HTTP requests (not WebSockets) 7 | * 8 | * For more information on configuration, check out: 9 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.http.html 10 | */ 11 | 12 | module.exports.http = { 13 | 14 | /**************************************************************************** 15 | * * 16 | * Express middleware to use for every Sails request. To add custom * 17 | * middleware to the mix, add a function to the middleware config object and * 18 | * add its key to the "order" array. The $custom key is reserved for * 19 | * backwards-compatibility with Sails v0.9.x apps that use the * 20 | * `customMiddleware` config option. * 21 | * * 22 | ****************************************************************************/ 23 | 24 | // middleware: { 25 | 26 | /*************************************************************************** 27 | * * 28 | * The order in which middleware should be run for HTTP request. (the Sails * 29 | * router is invoked by the "router" middleware below.) * 30 | * * 31 | ***************************************************************************/ 32 | 33 | // order: [ 34 | // 'startRequestTimer', 35 | // 'cookieParser', 36 | // 'session', 37 | // 'myRequestLogger', 38 | // 'bodyParser', 39 | // 'handleBodyParserError', 40 | // 'compress', 41 | // 'methodOverride', 42 | // 'poweredBy', 43 | // '$custom', 44 | // 'router', 45 | // 'www', 46 | // 'favicon', 47 | // '404', 48 | // '500' 49 | // ], 50 | 51 | /**************************************************************************** 52 | * * 53 | * Example custom middleware; logs each request to the console. * 54 | * * 55 | ****************************************************************************/ 56 | 57 | // myRequestLogger: function (req, res, next) { 58 | // console.log("Requested :: ", req.method, req.url); 59 | // return next(); 60 | // } 61 | 62 | 63 | /*************************************************************************** 64 | * * 65 | * The body parser that will handle incoming multipart HTTP requests. By * 66 | * default as of v0.10, Sails uses * 67 | * [skipper](http://github.com/balderdashy/skipper). See * 68 | * http://www.senchalabs.org/connect/multipart.html for other options. * 69 | * * 70 | ***************************************************************************/ 71 | 72 | // bodyParser: require('skipper') 73 | 74 | // }, 75 | 76 | /*************************************************************************** 77 | * * 78 | * The number of seconds to cache flat files on disk being served by * 79 | * Express static middleware (by default, these files are in `.tmp/public`) * 80 | * * 81 | * The HTTP static cache is only active in a 'production' environment, * 82 | * since that's the only time Express will cache flat-files. * 83 | * * 84 | ***************************************************************************/ 85 | 86 | // cache: 31557600000 87 | }; 88 | -------------------------------------------------------------------------------- /twitter/config/i18n.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Internationalization / Localization Settings 3 | * (sails.config.i18n) 4 | * 5 | * If your app will touch people from all over the world, i18n (or internationalization) 6 | * may be an important part of your international strategy. 7 | * 8 | * 9 | * For more informationom i18n in Sails, check out: 10 | * http://sailsjs.org/#!/documentation/concepts/Internationalization 11 | * 12 | * For a complete list of i18n options, see: 13 | * https://github.com/mashpie/i18n-node#list-of-configuration-options 14 | * 15 | * 16 | */ 17 | 18 | module.exports.i18n = { 19 | 20 | /*************************************************************************** 21 | * * 22 | * Which locales are supported? * 23 | * * 24 | ***************************************************************************/ 25 | 26 | // locales: ['en', 'es', 'fr', 'de'], 27 | 28 | /**************************************************************************** 29 | * * 30 | * What is the default locale for the site? Note that this setting will be * 31 | * overridden for any request that sends an "Accept-Language" header (i.e. * 32 | * most browsers), but it's still useful if you need to localize the * 33 | * response for requests made by non-browser clients (e.g. cURL). * 34 | * * 35 | ****************************************************************************/ 36 | 37 | // defaultLocale: 'en', 38 | 39 | /**************************************************************************** 40 | * * 41 | * Automatically add new keys to locale (translation) files when they are * 42 | * encountered during a request? * 43 | * * 44 | ****************************************************************************/ 45 | 46 | // updateFiles: false, 47 | 48 | /**************************************************************************** 49 | * * 50 | * Path (relative to app root) of directory to store locale (translation) * 51 | * files in. * 52 | * * 53 | ****************************************************************************/ 54 | 55 | // localesDirectory: '/config/locales' 56 | 57 | }; 58 | -------------------------------------------------------------------------------- /twitter/config/locales/_README.md: -------------------------------------------------------------------------------- 1 | # Internationalization / Localization Settings 2 | 3 | > Also see the official docs on internationalization/localization: 4 | > http://links.sailsjs.org/docs/config/locales 5 | 6 | ## Locales 7 | All locale files live under `config/locales`. Here is where you can add translations 8 | as JSON key-value pairs. The name of the file should match the language that you are supporting, which allows for automatic language detection based on request headers. 9 | 10 | Here is an example locale stringfile for the Spanish language (`config/locales/es.json`): 11 | ```json 12 | { 13 | "Hello!": "Hola!", 14 | "Hello %s, how are you today?": "¿Hola %s, como estas?", 15 | } 16 | ``` 17 | ## Usage 18 | Locales can be accessed in controllers/policies through `res.i18n()`, or in views through the `__(key)` or `i18n(key)` functions. 19 | Remember that the keys are case sensitive and require exact key matches, e.g. 20 | 21 | ```ejs 22 |

<%= __('Welcome to PencilPals!') %>

23 |

<%= i18n('Hello %s, how are you today?', 'Pencil Maven') %>

24 |

<%= i18n('That\'s right-- you can use either i18n() or __()') %>

25 | ``` 26 | 27 | ## Configuration 28 | Localization/internationalization config can be found in `config/i18n.js`, from where you can set your supported locales. 29 | -------------------------------------------------------------------------------- /twitter/config/locales/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "Welcome": "Willkommen", 3 | "A brand new app.": "Eine neue App." 4 | } 5 | -------------------------------------------------------------------------------- /twitter/config/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Welcome": "Welcome", 3 | "A brand new app.": "A brand new app." 4 | } 5 | -------------------------------------------------------------------------------- /twitter/config/locales/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "Welcome": "Bienvenido", 3 | "A brand new app.": "Una aplicación de la nueva marca." 4 | } 5 | -------------------------------------------------------------------------------- /twitter/config/locales/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Welcome": "Bienvenue", 3 | "A brand new app.": "Une toute nouvelle application." 4 | } 5 | -------------------------------------------------------------------------------- /twitter/config/log.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Built-in Log Configuration 3 | * (sails.config.log) 4 | * 5 | * Configure the log level for your app, as well as the transport 6 | * (Underneath the covers, Sails uses Winston for logging, which 7 | * allows for some pretty neat custom transports/adapters for log messages) 8 | * 9 | * For more information on the Sails logger, check out: 10 | * http://sailsjs.org/#!/documentation/concepts/Logging 11 | */ 12 | 13 | module.exports.log = { 14 | 15 | /*************************************************************************** 16 | * * 17 | * Valid `level` configs: i.e. the minimum log level to capture with * 18 | * sails.log.*() * 19 | * * 20 | * The order of precedence for log levels from lowest to highest is: * 21 | * silly, verbose, info, debug, warn, error * 22 | * * 23 | * You may also set the level to "silent" to suppress all logs. * 24 | * * 25 | ***************************************************************************/ 26 | 27 | // level: 'info' 28 | 29 | }; 30 | -------------------------------------------------------------------------------- /twitter/config/models.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Default model configuration 3 | * (sails.config.models) 4 | * 5 | * Unless you override them, the following properties will be included 6 | * in each of your models. 7 | * 8 | * For more info on Sails models, see: 9 | * http://sailsjs.org/#!/documentation/concepts/ORM 10 | */ 11 | 12 | module.exports.models = { 13 | 14 | /*************************************************************************** 15 | * * 16 | * Your app's default connection. i.e. the name of one of your app's * 17 | * connections (see `config/connections.js`) * 18 | * * 19 | ***************************************************************************/ 20 | connection: 'someMongodbServer', 21 | 22 | /*************************************************************************** 23 | * * 24 | * How and whether Sails will attempt to automatically rebuild the * 25 | * tables/collections/etc. in your schema. * 26 | * * 27 | * See http://sailsjs.org/#!/documentation/concepts/ORM/model-settings.html * 28 | * * 29 | ***************************************************************************/ 30 | migrate: 'safe' 31 | 32 | }; 33 | -------------------------------------------------------------------------------- /twitter/config/policies.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Policy Mappings 3 | * (sails.config.policies) 4 | * 5 | * Policies are simple functions which run **before** your controllers. 6 | * You can apply one or more policies to a given controller, or protect 7 | * its actions individually. 8 | * 9 | * Any policy file (e.g. `api/policies/authenticated.js`) can be accessed 10 | * below by its filename, minus the extension, (e.g. "authenticated") 11 | * 12 | * For more information on how policies work, see: 13 | * http://sailsjs.org/#!/documentation/concepts/Policies 14 | * 15 | * For more information on configuring policies, check out: 16 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.policies.html 17 | */ 18 | 19 | 20 | module.exports.policies = { 21 | 22 | /*************************************************************************** 23 | * * 24 | * Default policy for all controllers and actions (`true` allows public * 25 | * access) * 26 | * * 27 | ***************************************************************************/ 28 | 29 | // '*': true, 30 | 31 | /*************************************************************************** 32 | * * 33 | * Here's an example of mapping some policies to run before a controller * 34 | * and its actions * 35 | * * 36 | ***************************************************************************/ 37 | // RabbitController: { 38 | 39 | // Apply the `false` policy as the default for all of RabbitController's actions 40 | // (`false` prevents all access, which ensures that nothing bad happens to our rabbits) 41 | // '*': false, 42 | 43 | // For the action `nurture`, apply the 'isRabbitMother' policy 44 | // (this overrides `false` above) 45 | // nurture : 'isRabbitMother', 46 | 47 | // Apply the `isNiceToAnimals` AND `hasRabbitFood` policies 48 | // before letting any users feed our rabbits 49 | // feed : ['isNiceToAnimals', 'hasRabbitFood'] 50 | // } 51 | }; 52 | -------------------------------------------------------------------------------- /twitter/config/routes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Route Mappings 3 | * (sails.config.routes) 4 | * 5 | * Your routes map URLs to views and controllers. 6 | * 7 | * If Sails receives a URL that doesn't match any of the routes below, 8 | * it will check for matching files (images, scripts, stylesheets, etc.) 9 | * in your assets directory. e.g. `http://localhost:1337/images/foo.jpg` 10 | * might match an image file: `/assets/images/foo.jpg` 11 | * 12 | * Finally, if those don't match either, the default 404 handler is triggered. 13 | * See `api/responses/notFound.js` to adjust your app's 404 logic. 14 | * 15 | * Note: Sails doesn't ACTUALLY serve stuff from `assets`-- the default Gruntfile in Sails copies 16 | * flat files from `assets` to `.tmp/public`. This allows you to do things like compile LESS or 17 | * CoffeeScript for the front-end. 18 | * 19 | * For more information on configuring custom routes, check out: 20 | * http://sailsjs.org/#!/documentation/concepts/Routes/RouteTargetSyntax.html 21 | */ 22 | 23 | module.exports.routes = { 24 | 25 | /*************************************************************************** 26 | * * 27 | * Make the view located at `views/homepage.ejs` (or `views/homepage.jade`, * 28 | * etc. depending on your default view engine) your home page. * 29 | * * 30 | * (Alternatively, remove this and add an `index.html` file in your * 31 | * `assets` directory) * 32 | * * 33 | ***************************************************************************/ 34 | 35 | '/': { 36 | view: 'homepage' 37 | } 38 | 39 | /*************************************************************************** 40 | * * 41 | * Custom routes here... * 42 | * * 43 | * If a request to a URL doesn't match any of the custom routes above, it * 44 | * is matched against Sails route blueprints. See `config/blueprints.js` * 45 | * for configuration options and examples. * 46 | * * 47 | ***************************************************************************/ 48 | 49 | }; 50 | -------------------------------------------------------------------------------- /twitter/config/session.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Session Configuration 3 | * (sails.config.session) 4 | * 5 | * Sails session integration leans heavily on the great work already done by 6 | * Express, but also unifies Socket.io with the Connect session store. It uses 7 | * Connect's cookie parser to normalize configuration differences between Express 8 | * and Socket.io and hooks into Sails' middleware interpreter to allow you to access 9 | * and auto-save to `req.session` with Socket.io the same way you would with Express. 10 | * 11 | * For more information on configuring the session, check out: 12 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.session.html 13 | */ 14 | 15 | module.exports.session = { 16 | 17 | /*************************************************************************** 18 | * * 19 | * Session secret is automatically generated when your new app is created * 20 | * Replace at your own risk in production-- you will invalidate the cookies * 21 | * of your users, forcing them to log in again. * 22 | * * 23 | ***************************************************************************/ 24 | secret: 'b62dcc814e1defb9d984c4816979b23b', 25 | 26 | 27 | /*************************************************************************** 28 | * * 29 | * Set the session cookie expire time The maxAge is set by milliseconds, * 30 | * the example below is for 24 hours * 31 | * * 32 | ***************************************************************************/ 33 | 34 | // cookie: { 35 | // maxAge: 24 * 60 * 60 * 1000 36 | // }, 37 | 38 | /*************************************************************************** 39 | * * 40 | * In production, uncomment the following lines to set up a shared redis * 41 | * session store that can be shared across multiple Sails.js servers * 42 | ***************************************************************************/ 43 | 44 | // adapter: 'redis', 45 | 46 | /*************************************************************************** 47 | * * 48 | * The following values are optional, if no options are set a redis * 49 | * instance running on localhost is expected. Read more about options at: * 50 | * https://github.com/visionmedia/connect-redis * 51 | * * 52 | * * 53 | ***************************************************************************/ 54 | 55 | // host: 'localhost', 56 | // port: 6379, 57 | // ttl: , 58 | // db: 0, 59 | // pass: , 60 | // prefix: 'sess:', 61 | 62 | 63 | /*************************************************************************** 64 | * * 65 | * Uncomment the following lines to use your Mongo adapter as a session * 66 | * store * 67 | * * 68 | ***************************************************************************/ 69 | 70 | // adapter: 'mongo', 71 | // host: 'localhost', 72 | // port: 27017, 73 | // db: 'sails', 74 | // collection: 'sessions', 75 | 76 | /*************************************************************************** 77 | * * 78 | * Optional Values: * 79 | * * 80 | * # Note: url will override other connection settings url: * 81 | * 'mongodb://user:pass@host:port/database/collection', * 82 | * * 83 | ***************************************************************************/ 84 | 85 | // username: '', 86 | // password: '', 87 | // auto_reconnect: false, 88 | // ssl: false, 89 | // stringify: true 90 | 91 | }; 92 | -------------------------------------------------------------------------------- /twitter/config/sockets.js: -------------------------------------------------------------------------------- 1 | /** 2 | * WebSocket Server Settings 3 | * (sails.config.sockets) 4 | * 5 | * These settings provide transparent access to the options for Sails' 6 | * encapsulated WebSocket server, as well as some additional Sails-specific 7 | * configuration layered on top. 8 | * 9 | * For more information on sockets configuration, including advanced config options, see: 10 | * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.sockets.html 11 | */ 12 | 13 | module.exports.sockets = { 14 | 15 | 16 | /*************************************************************************** 17 | * * 18 | * Node.js (and consequently Sails.js) apps scale horizontally. It's a * 19 | * powerful, efficient approach, but it involves a tiny bit of planning. At * 20 | * scale, you'll want to be able to copy your app onto multiple Sails.js * 21 | * servers and throw them behind a load balancer. * 22 | * * 23 | * One of the big challenges of scaling an application is that these sorts * 24 | * of clustered deployments cannot share memory, since they are on * 25 | * physically different machines. On top of that, there is no guarantee * 26 | * that a user will "stick" with the same server between requests (whether * 27 | * HTTP or sockets), since the load balancer will route each request to the * 28 | * Sails server with the most available resources. However that means that * 29 | * all room/pubsub/socket processing and shared memory has to be offloaded * 30 | * to a shared, remote messaging queue (usually Redis) * 31 | * * 32 | * Luckily, Socket.io (and consequently Sails.js) apps support Redis for * 33 | * sockets by default. To enable a remote redis pubsub server, uncomment * 34 | * the config below. * 35 | * * 36 | * Worth mentioning is that, if `adapter` config is `redis`, but host/port * 37 | * is left unset, Sails will try to connect to redis running on localhost * 38 | * via port 6379 * 39 | * * 40 | ***************************************************************************/ 41 | // adapter: 'memory', 42 | 43 | // 44 | // -OR- 45 | // 46 | 47 | // adapter: 'redis', 48 | // host: '127.0.0.1', 49 | // port: 6379, 50 | // db: 'sails', 51 | // pass: '', 52 | 53 | 54 | 55 | /*************************************************************************** 56 | * * 57 | * Whether to expose a 'get /__getcookie' route with CORS support that sets * 58 | * a cookie (this is used by the sails.io.js socket client to get access to * 59 | * a 3rd party cookie and to enable sessions). * 60 | * * 61 | * Warning: Currently in this scenario, CORS settings apply to interpreted * 62 | * requests sent via a socket.io connection that used this cookie to * 63 | * connect, even for non-browser clients! (e.g. iOS apps, toasters, node.js * 64 | * unit tests) * 65 | * * 66 | ***************************************************************************/ 67 | 68 | // grant3rdPartyCookie: true, 69 | 70 | 71 | 72 | /*************************************************************************** 73 | * * 74 | * `beforeConnect` * 75 | * * 76 | * This custom beforeConnect function will be run each time BEFORE a new * 77 | * socket is allowed to connect, when the initial socket.io handshake is * 78 | * performed with the server. * 79 | * * 80 | * By default, when a socket tries to connect, Sails allows it, every time. * 81 | * (much in the same way any HTTP request is allowed to reach your routes. * 82 | * If no valid cookie was sent, a temporary session will be created for the * 83 | * connecting socket. * 84 | * * 85 | * If the cookie sent as part of the connection request doesn't match any * 86 | * known user session, a new user session is created for it. * 87 | * * 88 | * In most cases, the user would already have a cookie since they loaded * 89 | * the socket.io client and the initial HTML page you're building. * 90 | * * 91 | * However, in the case of cross-domain requests, it is possible to receive * 92 | * a connection upgrade request WITHOUT A COOKIE (for certain transports) * 93 | * In this case, there is no way to keep track of the requesting user * 94 | * between requests, since there is no identifying information to link * 95 | * him/her with a session. The sails.io.js client solves this by connecting * 96 | * to a CORS/jsonp endpoint first to get a 3rd party cookie(fortunately this* 97 | * works, even in Safari), then opening the connection. * 98 | * * 99 | * You can also pass along a ?cookie query parameter to the upgrade url, * 100 | * which Sails will use in the absence of a proper cookie e.g. (when * 101 | * connecting from the client): * 102 | * io.sails.connect('http://localhost:1337?cookie=smokeybear') * 103 | * * 104 | * Finally note that the user's cookie is NOT (and will never be) accessible* 105 | * from client-side javascript. Using HTTP-only cookies is crucial for your * 106 | * app's security. * 107 | * * 108 | ***************************************************************************/ 109 | // beforeConnect: function(handshake, cb) { 110 | // // `true` allows the connection 111 | // return cb(null, true); 112 | // 113 | // // (`false` would reject the connection) 114 | // }, 115 | 116 | 117 | /*************************************************************************** 118 | * * 119 | * `afterDisconnect` * 120 | * * 121 | * This custom afterDisconnect function will be run each time a socket * 122 | * disconnects * 123 | * * 124 | ***************************************************************************/ 125 | // afterDisconnect: function(session, socket, cb) { 126 | // // By default: do nothing. 127 | // return cb(); 128 | // }, 129 | 130 | /*************************************************************************** 131 | * * 132 | * `transports` * 133 | * * 134 | * A array of allowed transport methods which the clients will try to use. * 135 | * On server environments that don't support sticky sessions, the "polling" * 136 | * transport should be disabled. * 137 | * * 138 | ***************************************************************************/ 139 | // transports: ["polling", "websocket"] 140 | 141 | }; 142 | -------------------------------------------------------------------------------- /twitter/config/views.js: -------------------------------------------------------------------------------- 1 | /** 2 | * View Engine Configuration 3 | * (sails.config.views) 4 | * 5 | * Server-sent views are a classic and effective way to get your app up 6 | * and running. Views are normally served from controllers. Below, you can 7 | * configure your templating language/framework of choice and configure 8 | * Sails' layout support. 9 | * 10 | * For more information on views and layouts, check out: 11 | * http://sailsjs.org/#!/documentation/concepts/Views 12 | */ 13 | 14 | module.exports.views = { 15 | 16 | /**************************************************************************** 17 | * * 18 | * View engine (aka template language) to use for your app's *server-side* * 19 | * views * 20 | * * 21 | * Sails+Express supports all view engines which implement TJ Holowaychuk's * 22 | * `consolidate.js`, including, but not limited to: * 23 | * * 24 | * ejs, jade, handlebars, mustache underscore, hogan, haml, haml-coffee, * 25 | * dust atpl, eco, ect, jazz, jqtpl, JUST, liquor, QEJS, swig, templayed, * 26 | * toffee, walrus, & whiskers * 27 | * * 28 | * For more options, check out the docs: * 29 | * https://github.com/balderdashy/sails-wiki/blob/0.9/config.views.md#engine * 30 | * * 31 | ****************************************************************************/ 32 | 33 | engine: 'ejs', 34 | 35 | 36 | /**************************************************************************** 37 | * * 38 | * Layouts are simply top-level HTML templates you can use as wrappers for * 39 | * your server-side views. If you're using ejs or jade, you can take * 40 | * advantage of Sails' built-in `layout` support. * 41 | * * 42 | * When using a layout, when one of your views is served, it is injected * 43 | * into the `body` partial defined in the layout. This lets you reuse header * 44 | * and footer logic between views. * 45 | * * 46 | * NOTE: Layout support is only implemented for the `ejs` view engine! * 47 | * For most other engines, it is not necessary, since they implement * 48 | * partials/layouts themselves. In those cases, this config will be * 49 | * silently ignored. * 50 | * * 51 | * The `layout` setting may be set to one of the following: * 52 | * * 53 | * If `false`, layouts will be disabled. Otherwise, if a string is * 54 | * specified, it will be interpreted as the relative path to your layout * 55 | * file from `views/` folder. (the file extension, ".ejs", should be * 56 | * omitted) * 57 | * * 58 | ****************************************************************************/ 59 | 60 | /**************************************************************************** 61 | * * 62 | * Using Multiple Layouts * 63 | * * 64 | * If you're using the default `ejs` or `handlebars` Sails supports the use * 65 | * of multiple `layout` files. To take advantage of this, before rendering a * 66 | * view, override the `layout` local in your controller by setting * 67 | * `res.locals.layout`. (this is handy if you parts of your app's UI look * 68 | * completely different from each other) * 69 | * * 70 | * e.g. your default might be * 71 | * layout: 'layouts/public' * 72 | * * 73 | * But you might override that in some of your controllers with: * 74 | * layout: 'layouts/internal' * 75 | * * 76 | ****************************************************************************/ 77 | 78 | layout: 'layout', 79 | 80 | /**************************************************************************** 81 | * * 82 | * Partials are simply top-level snippets you can leverage to reuse template * 83 | * for your server-side views. If you're using handlebars, you can take * 84 | * advantage of Sails' built-in `partials` support. * 85 | * * 86 | * If `false` or empty partials will be located in the same folder as views. * 87 | * Otherwise, if a string is specified, it will be interpreted as the * 88 | * relative path to your partial files from `views/` folder. * 89 | * * 90 | ****************************************************************************/ 91 | 92 | partials: false 93 | 94 | 95 | }; -------------------------------------------------------------------------------- /twitter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "twitter-app", 3 | "private": true, 4 | "version": "0.1.0", 5 | "description": "a Sails application", 6 | "keywords": [], 7 | "dependencies": { 8 | "apparatus": "0.0.10", 9 | "bluebird": "^3.7.1", 10 | "ejs": "~2.7.1", 11 | "firebase": "^7.2.1", 12 | "forever": "^1.0.0", 13 | "grunt": "1.0.4", 14 | "grunt-contrib-clean": "~2.0.0", 15 | "grunt-contrib-coffee": "~2.1.0", 16 | "grunt-contrib-concat": "~1.0.1", 17 | "grunt-contrib-copy": "~1.0.0", 18 | "grunt-contrib-cssmin": "~3.0.0", 19 | "grunt-contrib-jst": "~1.0.0", 20 | "grunt-contrib-less": "2.0.0", 21 | "grunt-contrib-uglify": "~4.0.1", 22 | "grunt-contrib-watch": "~1.1.0", 23 | "grunt-sails-linker": "~1.0.4", 24 | "grunt-sync": "~0.8.1", 25 | "include-all": "~4.0.3", 26 | "natural": "^0.6.3", 27 | "pos": "^0.4.2", 28 | "rc": "~1.2.8", 29 | "sails": "0.12.14", 30 | "sails-disk": "~1.1.2", 31 | "sails-mongo": "^0.12.3", 32 | "sentiment": "^5.0.2", 33 | "twitter": "^1.7.1", 34 | "underscore": "^1.9.1" 35 | }, 36 | "scripts": { 37 | "debug": "node debug app.js", 38 | "start": "node app.js" 39 | }, 40 | "main": "app.js", 41 | "repository": { 42 | "type": "git", 43 | "url": "git://github.com/vishwajeetv/twitter.git" 44 | }, 45 | "author": "vishwajeetv", 46 | "license": "MIT", 47 | "bugs": { 48 | "url": "https://github.com/vishwajeetv/twitter/issues" 49 | }, 50 | "homepage": "https://github.com/vishwajeetv/twitter#readme", 51 | "devDependencies": {} 52 | } 53 | -------------------------------------------------------------------------------- /twitter/views/403.ejs: -------------------------------------------------------------------------------- 1 | 2 | 36 | 37 | 38 | Forbidden 39 | 40 | 44 | 45 | 46 | 47 |
48 |
49 | 50 |
51 | 52 |
53 |

54 | Forbidden 55 |

56 |

57 | <% if (typeof error !== 'undefined') { %> 58 | <%= error %> 59 | <% } else { %> 60 | You don't have permission to see the page you're trying to reach. 61 | <% } %> 62 |

63 |

64 | Why might this be happening? 65 |

66 |
67 | 68 | 73 |
74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /twitter/views/404.ejs: -------------------------------------------------------------------------------- 1 | 2 | 36 | 37 | 38 | Page Not Found 39 | 40 | 44 | 45 | 46 | 47 |
48 |
49 | 50 |
51 | 52 |
53 |

54 | Something's fishy here. 55 |

56 |

57 | <% if (typeof error!== 'undefined') { %> 58 | <%= error %> 59 | <% } else { %> 60 | The page you were trying to reach doesn't exist. 61 | <% } %> 62 |

63 |

64 | Why might this be happening? 65 |

66 |
67 | 68 | 73 |
74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /twitter/views/homepage.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 12 | 13 |
14 |
15 |

<%= __('A brand new app.') %>

16 |

You're looking at: <%= view.pathFromApp + '.' +view.ext %>

17 |
18 |
19 | 21 |
    22 |
  • 23 |
    24 |
    25 |

    Generate a REST API.

    26 |

    27 | Run sails generate api user. This will create two files: a model api/models/User.js and a controllerapi/controllers/UserController.js. 28 |

    29 |
    30 |
  • 31 |
  • 32 |
    33 |
    34 |

    35 | Lift your app. 36 |

    37 |

    38 | Run sails lift to start up your app server. If you visit http://localhost:1337/user in your browser, you'll see a WebSocket-compatible user API. 39 |

    40 |
    41 |
  • 42 |
  • 43 |
    44 |
    45 |

    46 | Dive in. 47 |

    48 |

    Blueprints are just the beginning. You'll probably also want to learn how to customize your app's routes, set up security policies, configure your data sources, and build custom controller actions. For more help getting started, check out the links on this page.

    49 | 50 |
    51 |
  • 52 |
53 | 73 |
74 |
75 | -------------------------------------------------------------------------------- /twitter/views/layout.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | New Sails App 5 | 6 | 7 | 8 | 9 | 10 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | <%- body %> 37 | 38 | 39 | 40 | 60 | 61 | 62 | 63 | 64 | 65 | 87 | 88 | 89 | 90 | 91 | 92 | --------------------------------------------------------------------------------