" I represent the overall activity of given twitter profile, in terms of the kind of messages this profiles emits, being them: tweets, retweets and replies. " Class { #name : #TwitterProfileOverview, #superclass : #RTBuilder, #instVars : [ 'avatar', 'messages', 'screenName', 'repliedTo', 'retweetedTo', 'interactionProfiles', 'database' ], #category : #'Dataviz-Twitter' } { #category : #accessing } TwitterProfileOverview >> avatar [ ^ avatar form ] { #category : #accessing } TwitterProfileOverview >> avatar: aBitMap [ avatar := aBitMap ] { #category : #'data visualization' } TwitterProfileOverview >> avatarWheel [ "I show the profile avatar surrounded by a wheel representing the proportion between tweets, retweets and replies of such profile." | b | self messages ifEmpty: [^ self]. b := RTPieBuilder new. b interaction popup. b shape current innerRadius: 150; externalRadius: 165. b objects: self messages splitByType values. b slice: #size. b normalizer distinctColor. self avatar ifNotNil: [ b view canvas addShape: (TRBitmapShape new form: self avatar) ]. ^ b view. ] { #category : #examples } TwitterProfileOverview >> avatarWheelExample [ | o p | o := self class new. o screenName: 'ObjectProfile'. o messages tweetsSize: 200; retweetsSize: 70; repliesSize: 15. p := TwitterProfile new scrapDataForProfile: o screenName. o avatar: p avatar. ^ o avatarWheel ] { #category : #accessing } TwitterProfileOverview >> database [ | dataLocation | (FileLocator documents / 'Grafoscopio') ensureCreateDirectory. (FileLocator documents / 'Grafoscopio' / 'Projects') ensureCreateDirectory. (FileLocator documents / 'Grafoscopio' / 'Projects' / 'DataSelfies') ensureCreateDirectory. dataLocation := FileLocator documents / 'Grafoscopio' / 'Projects' / 'DataSelfies' / 'data-selfies.sqlite'. database := UDBCSQLite3Connection on: dataLocation fullName. ^ database ] { #category : #accessing } TwitterProfileOverview >> database: anObject [ database := anObject ] { #category : #persistence } TwitterProfileOverview >> exportInteractionProfilesTo: aFileReference [ STON put: self interactionProfiles onStreamPretty: aFileReference writeStream ] { #category : #'data visualization' } TwitterProfileOverview >> frequentTweetedWordsShown: aWorldAmount retweetedProfilesShown: rtProfilesAmount mentionedProfilesShown: mtProfilesAmount [ "I put all the avatar wheel with tags that come from predefined amounts" | aColorPalette | aColorPalette := { Color orange . Color black . Color red }. ^ self frequentTweetedWordsShown: aWorldAmount retweetedProfilesShown: rtProfilesAmount mentionsProfilesShown: mtProfilesAmount coloredWith: aColorPalette ] { #category : #'data visualization' } TwitterProfileOverview >> frequentTweetedWordsShown: aWordsPercentage retweetedProfilesShown: rtProfilesPercentage mentionsProfilesShown: mtProfilesPercentage coloredWith: aColorPalette [ "I put all the avatar wheel with tags that come from predefined amounts" | tweetsArc retweetArc mentionsArc tweetsWords retweetedProfiles mentionedProfiles visualWords arcs queries canvasTemp | self messages ifNil: [ ^ self ]. tweetsArc := self avatarWheel canvas shapes at: 2. retweetArc := self avatarWheel canvas shapes at: 1. mentionsArc := self avatarWheel canvas shapes at: 3. tweetsWords := self messages wordsByFrequencyInTweetsUpTo: aWordsPercentage. retweetedProfiles := self messages retweetedProfilesByFrequencyUpTo: rtProfilesPercentage. mentionedProfiles := self messages mentionedProfilesByFrequencyUpTo: mtProfilesPercentage. arcs := { tweetsArc . retweetArc . mentionsArc }. queries := { tweetsWords . mentionedProfiles . retweetedProfiles }. canvasTemp := self avatarWheel canvas. arcs doWithIndex: [ :arc :i | visualWords := TPMessages new visualWordsFrom: (queries at: i) colored: (aColorPalette at: i). arc color: (aColorPalette at: i). arc surroundedBy: visualWords radialGap: 85 angularGap: 3 renderedIn: canvasTemp. canvasTemp := canvasTemp ]. ^ canvasTemp ] { #category : #'data scrapping' } TwitterProfileOverview >> getAvatarForProfile: aTwitterProfile [ "I scrap the avatar image for aTwitterProfile and use it to fill my avatar form. aTwitterProfile is the twitter's screen name, the string after the '@' character" self avatar: (TwitterProfile new scrapAvatarForProfile: aTwitterProfile) ] { #category : #persistence } TwitterProfileOverview >> importIProfilesFromDB [ | query answer | query := 'SELECT * FROM interaction_profiles;'. answer := (self database open execute: query) rows collect: [ :each | each data ]. self database isOpen ifTrue: [ self database close ]. answer do: [ :each | self interactionProfiles add: (TwitterInteractionProfile new screenName: (each at: 'screenName'); avatar: (FLMaterializer materializeFromByteArray: (each at: 'avatar')); tweets: (each at: 'tweets'); retweets: (each at: 'retweets') ) ] ] { #category : #persistence } TwitterProfileOverview >> importInteractionProfilesFrom: aFileReference [ STONReader fromSton: aFileReference contents ] { #category : #initialization } TwitterProfileOverview >> initialize [ super initialize. avatar := RTBitmap new. messagesStats := TPMessages new. screenName := String new. ] { #category : #accessing } TwitterProfileOverview >> interactionProfiles [ ^ interactionProfiles ifNil: [ interactionProfiles := OrderedCollection new ] ] { #category : #accessing } TwitterProfileOverview >> interactionProfiles: anObject [ interactionProfiles := anObject ] { #category : #persistence } TwitterProfileOverview >> materializeIProfilesFrom: aFileReference [ self interactionProfiles: (FLMaterializer materializationFromFileNamed: aFileReference fullName) root ] { #category : #accessing } TwitterProfileOverview >> messages [ ^ messages ifNil: [ messages := TwitterMessages new ] ] { #category : #utility } TwitterProfileOverview >> putAvatarsOnInteractionProfiles [ self interactionProfiles size isZero ifTrue: [ ^ self ]. self interactionProfiles do: [ :each | each avatar: (TwitterProfile new scrapAvatarForProfile: each screenName) ] ] { #category : #utility } TwitterProfileOverview >> putTweetsOnInteractionProfiles [ self sortedRetweetedTo keysAndValuesDo: [ :k :v | | ip| ip := TwitterInteractionProfile new screenName: k; tweets: v. self interactionProfiles add: ip ]. ] { #category : #accessing } TwitterProfileOverview >> repliedTo [ ^ repliedTo ifNil: [ repliedTo := OrderedCollection new ] ] { #category : #accessing } TwitterProfileOverview >> repliedTo: anObject [ repliedTo := anObject ] { #category : #accessing } TwitterProfileOverview >> retweetedTo [ ^ retweetedTo ifNil: [ ^ retweetedTo := OrderedCollection new ] ] { #category : #accessing } TwitterProfileOverview >> retweetedTo: anObject [ reposted := anObject ] { #category : #accessing } TwitterProfileOverview >> screenName [ ^ screenName ] { #category : #accessing } TwitterProfileOverview >> screenName: aString [ screenName := aString ] { #category : #'data queries' } TwitterProfileOverview >> selectRepliesFrom: aJSONFile [ | allMessages | allMessages := NeoJSONReader fromString: (aJSONFile readStream nextLine; upToEnd) contents. self messages replies: (allMessages select: [ :each | each keys includes: 'in_reply_to_screen_name']). ^ self messages replies ] { #category : #'data queries' } TwitterProfileOverview >> selectRetweetsFrom: aJSONFile [ | allMessages | allMessages := NeoJSONReader fromString: (aJSONFile readStream nextLine; upToEnd) contents. self messages retweets: (allMessages select: [ :each | each keys includes: 'retweeted_status']). ^ self messages retweets ] { #category : #persistence } TwitterProfileOverview >> serializeIProfilesTo: aFileReference [ FLSerializer newDefault serialize: self interactionProfiles toFileNamed: aFileReference fullName ] { #category : #'data queries' } TwitterProfileOverview >> splitMessagesByTypeFrom: aJSONFile [ ^ self messages splitByType ] { #category : #'data visualization' } TwitterProfileOverview >> taggedWheelFull [ "I put all the avatar wheel with tags that come from predefined amounts" self messages ifNil: [ ^self ]. self frequentTweetedWordsShown: self messages tweetsSize retweetedProfilesShown: self messages retweetsSize mentionedProfilesShown: self messages repliesSize ] { #category : #persistence } TwitterProfileOverview >> updateDBWithInteractionProfiles [ | db | db := UDBCSQLite3Connection on: self database. db open. db execute: 'CREATE TABLE IF NOT EXISTS interaction_profiles ( screenName text PRIMARY KEY, avatar blob, tweets integer, retweets integer );'. self interactionProfiles do: [ :ip | db execute: 'INSERT INTO interaction_profiles values (?, ?, ?, ?);' with: { ip screenName . ip avatar . ip tweets . ip retweets} ]. db close. ]