Compare commits
186 Commits
detach-rec
...
master
Author | SHA1 | Date | |
---|---|---|---|
ac5061e40d | |||
abc2752292 | |||
27a8a66dd3 | |||
8843a31d5d | |||
d6824d0c97 | |||
ae92892504 | |||
fd1de5971b | |||
03355de4f4 | |||
6e69fa5b3a | |||
635dc71ab5 | |||
57186f444f | |||
6d5caf6d6d | |||
43662d3fce | |||
7d77d26691 | |||
5336e8d224 | |||
69375d2683 | |||
3148f61eb9 | |||
625e66f006 | |||
f82728d159 | |||
5c82dd485a | |||
9348faaa91 | |||
1c551148fa | |||
4c8d269712 | |||
7b7fbceedc | |||
6b70551210 | |||
ff457ddbe7 | |||
ff72e94bb5 | |||
7bb4d66d57 | |||
482834e205 | |||
7c546c32b8 | |||
fa694f99a7 | |||
485572a81e | |||
ff38d34dc4 | |||
4d439283b1 | |||
7da6f87daf | |||
2cec4816a3 | |||
c91c793683 | |||
0af6e835b2 | |||
29b758c58a | |||
fdeecc5fde | |||
215e83d66e | |||
6548c70291 | |||
61eb15f8e3 | |||
6f821dfe53 | |||
5333b1c8d8 | |||
0b172e6f29 | |||
d4432bae7a | |||
fb00701e9e | |||
d2025d8aa9 | |||
1dec7081e4 | |||
33dccbc851 | |||
c8e7509e6a | |||
40d47caf1c | |||
7ddbed96bc | |||
3d7cc8110c | |||
68333ff0c0 | |||
dcb098e0c7 | |||
722b2d2066 | |||
07516e4dc6 | |||
773d94383c | |||
c535993e47 | |||
0577beb724 | |||
dc446f2026 | |||
8d9a66e012 | |||
8f3c63dc71 | |||
94b104e0e8 | |||
91744733bd | |||
c713ad9d28 | |||
3ad7148e62 | |||
5fdad7fa69 | |||
cb8bdbbaee | |||
e9d8745818 | |||
33805fd903 | |||
9544a92392 | |||
7a7058f30c | |||
cee2682e7a | |||
71484b16b0 | |||
f9b7fdf6bb | |||
75290c2538 | |||
bc6a4fb9eb | |||
7fa248e4fa | |||
f467129c42 | |||
7717f00185 | |||
9ae819722f | |||
7c6d7d9d80 | |||
463781085b | |||
a515a24a81 | |||
a61de2ecb7 | |||
742bb97446 | |||
bba0f9088d | |||
ac5d935ce1 | |||
0f62345220 | |||
624198091e | |||
33013dc52f | |||
390a6c3ef1 | |||
ac627f756c | |||
f898af0c05 | |||
fa1cf4a6d8 | |||
c16f109067 | |||
7d6c8259f6 | |||
defb1a8819 | |||
49f44385d7 | |||
c32a351932 | |||
459502cd5f | |||
4abb9fe987 | |||
a2d5d80e57 | |||
a08073232d | |||
35d9d037d3 | |||
ae652fdf3a | |||
21b4f432c4 | |||
419aa176ab | |||
6a628fddd3 | |||
aa5cf9cbba | |||
104fe9e348 | |||
06b0d5748d | |||
ac963c753f | |||
d2d0f739a8 | |||
ad26072420 | |||
bd69b73529 | |||
b964460fa3 | |||
a38124638d | |||
3ff8dfc86a | |||
bd279b1fd3 | |||
bbfae14949 | |||
f66d93e2ca | |||
57adfca53a | |||
66298fc248 | |||
cde55509bf | |||
e840f733fe | |||
1e06d16801 | |||
e43b375a6b | |||
e8373fab2c | |||
4e6b134848 | |||
99a03c0ad1 | |||
4ab8c6f0dd | |||
b92102a9d1 | |||
c2078d032f | |||
dc945dd8f9 | |||
2383ea4459 | |||
29c6deb617 | |||
3a58d281e2 | |||
a687e1b688 | |||
e98fd45083 | |||
906b29a7ef | |||
8d87ee3ed7 | |||
bfa2a2ab99 | |||
ad592beaf8 | |||
49d5529934 | |||
7de47a4959 | |||
ee9050a9b9 | |||
50a3ef901b | |||
5d457e824a | |||
f28e884a79 | |||
c9d9ec9620 | |||
1f081f58cf | |||
2de7cf1d11 | |||
986f4a83bc | |||
382dcd9954 | |||
0da5c06527 | |||
d61490e96c | |||
216832c3e2 | |||
a50e37dce1 | |||
4341383a8c | |||
f97b193c5e | |||
b9eedc6ec3 | |||
02ab2bd74a | |||
f563f964b2 | |||
8f2ed277fb | |||
a445100e58 | |||
bcf67809a8 | |||
2c61055b38 | |||
52b90dd8dd | |||
9b99ed0e33 | |||
db1a8e0502 | |||
bc168c4f0a | |||
65c36e924c | |||
89a73603a3 | |||
2c2ee3b132 | |||
94ce588376 | |||
01a62b2b2a | |||
36c261d534 | |||
a84e9504f9 | |||
d9af46ec37 | |||
e8f9e1ddd9 | |||
1e4f3d1190 | |||
b198bde247 |
@ -5,10 +5,13 @@ baseline: spec
|
|||||||
for: #common
|
for: #common
|
||||||
do: [
|
do: [
|
||||||
"Dependencies"
|
"Dependencies"
|
||||||
self xmlParserHTML: spec.
|
"self xmlParserHTML: spec."
|
||||||
"self rssTools: spec."
|
self reStore: spec.
|
||||||
|
self miniDocs: spec.
|
||||||
|
"self roassal3Exporters: spec."
|
||||||
|
"self tiddlyWikiPharo: spec."
|
||||||
"Packages"
|
"Packages"
|
||||||
spec
|
spec
|
||||||
package: 'Socialmetrica'
|
package: 'Socialmetrica'
|
||||||
with: [ spec requires: #('XMLParserHTML' "'RSSTools'") ]
|
with: [ spec requires: #('ReStore' 'MiniDocs' "'XMLParserHTML' 'Roassal3Exporters' 'TiddlyWikiPharo'") ]
|
||||||
]
|
]
|
@ -0,0 +1,8 @@
|
|||||||
|
accessing
|
||||||
|
miniDocs: spec
|
||||||
|
|
||||||
|
| repo |
|
||||||
|
repo := ExoRepo new
|
||||||
|
repository: 'https://code.sustrato.red/Offray/MiniDocs'.
|
||||||
|
repo load.
|
||||||
|
spec baseline: 'MiniDocs' with: [ spec repository: 'gitlocal://', repo local fullName ]
|
@ -0,0 +1,10 @@
|
|||||||
|
baselines
|
||||||
|
reStore: spec
|
||||||
|
|
||||||
|
Metacello new
|
||||||
|
repository: 'github://rko281/ReStoreForPharo';
|
||||||
|
baseline: 'ReStore';
|
||||||
|
onConflict: [ :ex | ex useLoaded ];
|
||||||
|
onWarningLog;
|
||||||
|
load: 'all'.
|
||||||
|
spec baseline: 'ReStore' with: [ spec repository: 'github://rko281/ReStoreForPharo']
|
@ -0,0 +1,7 @@
|
|||||||
|
baselines
|
||||||
|
roassal3Exporters: spec
|
||||||
|
Metacello new
|
||||||
|
baseline: 'Roassal3Exporters';
|
||||||
|
repository: 'github://ObjectProfile/Roassal3Exporters';
|
||||||
|
load.
|
||||||
|
spec baseline: 'Roassal3Exporters' with: [ spec repository: 'github://ObjectProfile/Roassal3Exporters']
|
@ -1,10 +0,0 @@
|
|||||||
baselines
|
|
||||||
rssTools: spec
|
|
||||||
Metacello new
|
|
||||||
repository: 'github://brackendev/RSSTools-Pharo:v1.0.1/src';
|
|
||||||
baseline: 'RSSTools';
|
|
||||||
onConflict: [ :ex | ex useLoaded ];
|
|
||||||
onUpgrade: [ :ex | ex useLoaded ];
|
|
||||||
onDowngrade: [ :ex | ex useLoaded ];
|
|
||||||
load.
|
|
||||||
spec baseline: 'RSSTools' with: [ spec repository: 'github://brackendev/RSSTools-Pharo:v1.0.1/src']
|
|
@ -0,0 +1,8 @@
|
|||||||
|
baselines
|
||||||
|
tiddlyWikiPharo: spec
|
||||||
|
|
||||||
|
| repo |
|
||||||
|
repo := ExoRepo new
|
||||||
|
repository: 'https://code.sustrato.red/Offray/TiddlyWikiPharo'.
|
||||||
|
repo load.
|
||||||
|
spec baseline: 'TiddlyWikiPharo' with: [ spec repository: 'gitlocal://', repo local fullName ]
|
@ -4,8 +4,6 @@ xmlParserHTML: spec
|
|||||||
baseline: 'XMLParserHTML';
|
baseline: 'XMLParserHTML';
|
||||||
repository: 'github://pharo-contributions/XML-XMLParserHTML/src';
|
repository: 'github://pharo-contributions/XML-XMLParserHTML/src';
|
||||||
onConflict: [ :ex | ex useLoaded ];
|
onConflict: [ :ex | ex useLoaded ];
|
||||||
onUpgrade: [ :ex | ex useLoaded ];
|
|
||||||
onDowngrade: [ :ex | ex useLoaded ];
|
|
||||||
onWarningLog;
|
onWarningLog;
|
||||||
load.
|
load.
|
||||||
spec baseline: 'XMLParserHTML' with: [spec repository: 'github://pharo-contributions/XML-XMLParserHTML/src']
|
spec baseline: 'XMLParserHTML' with: [spec repository: 'github://pharo-contributions/XML-XMLParserHTML/src']
|
@ -6,7 +6,7 @@ To install, first install [ExoRepo](https://code.tupale.co/Offray/ExoRepo) and t
|
|||||||
|
|
||||||
```smalltalk
|
```smalltalk
|
||||||
ExoRepo new
|
ExoRepo new
|
||||||
repository: 'https://code.tupale.co/Offray/Socialmetrica';
|
repository: 'https://code.sustrato.red/Offray/Socialmetrica';
|
||||||
load.
|
load.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
*Socialmetrica
|
||||||
|
periodsSince: startingDate until: endingDate
|
||||||
|
| borders subperiodDuration |
|
||||||
|
subperiodDuration := (endingDate - startingDate) / self.
|
||||||
|
borders := OrderedCollection new.
|
||||||
|
borders add: startingDate.
|
||||||
|
1 to: self do: [ :i | | ending |
|
||||||
|
ending := startingDate + (subperiodDuration * i).
|
||||||
|
borders add: ending.
|
||||||
|
].
|
||||||
|
^ borders
|
3
Socialmetrica.package/Integer.extension/properties.json
Normal file
3
Socialmetrica.package/Integer.extension/properties.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"name" : "Integer"
|
||||||
|
}
|
0
Socialmetrica.package/Nitter.class/README.md
Normal file
0
Socialmetrica.package/Nitter.class/README.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
accessing
|
||||||
|
columnsDictionary
|
||||||
|
^ {
|
||||||
|
'url' -> 'url' .
|
||||||
|
'healthy' -> 'healthy'.
|
||||||
|
'healthy_percentage_overall' -> 'uptime' .
|
||||||
|
'rss' -> 'rss' .
|
||||||
|
'version' -> 'version'} asDictionary
|
@ -0,0 +1,4 @@
|
|||||||
|
accessing
|
||||||
|
exportInstanceReport
|
||||||
|
MiniDocs exportAsSton: self instances on: FileLocator temp / 'instances.ston'.
|
||||||
|
^ FileLocator temp / 'instances.ston'
|
8
Socialmetrica.package/Nitter.class/class/instanceRows.st
Normal file
8
Socialmetrica.package/Nitter.class/class/instanceRows.st
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
accessing
|
||||||
|
instanceRows
|
||||||
|
^ (self instances at: 'hosts') collect: [:rawRow | | newRow |
|
||||||
|
newRow := NitterInstance new.
|
||||||
|
self columnsDictionary keysAndValuesDo: [:key :value |
|
||||||
|
newRow writeSlotNamed: value value: (rawRow at: key) ].
|
||||||
|
newRow
|
||||||
|
].
|
3
Socialmetrica.package/Nitter.class/class/instances.st
Normal file
3
Socialmetrica.package/Nitter.class/class/instances.st
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
accessing
|
||||||
|
instances
|
||||||
|
^ self instanceRows
|
@ -0,0 +1,9 @@
|
|||||||
|
accessing
|
||||||
|
instancesCache
|
||||||
|
| cache cacheFile |
|
||||||
|
cacheFile := FileLocator temp / 'nitter-instances.ston'.
|
||||||
|
cacheFile exists
|
||||||
|
ifFalse: [
|
||||||
|
cache := STON fromString: 'https://status.d420.de/api/v1/instances' asUrl retrieveContents.
|
||||||
|
MarkupFile exportAsFileOn: cacheFile containing: cache ].
|
||||||
|
^ STON fromString: cacheFile contents
|
@ -0,0 +1,2 @@
|
|||||||
|
accessing
|
||||||
|
instancesTable
|
@ -0,0 +1,21 @@
|
|||||||
|
accessing
|
||||||
|
viewInstancesFor: aView
|
||||||
|
<gtView>
|
||||||
|
| columnedList columnNamesMap |
|
||||||
|
self instances isEmptyOrNil ifTrue: [ ^ aView empty].
|
||||||
|
columnedList := aView columnedList
|
||||||
|
title: 'Instances';
|
||||||
|
items: [ self instanceRows ];
|
||||||
|
priority: 1.
|
||||||
|
columnNamesMap := {
|
||||||
|
'Instance' -> 'url'.
|
||||||
|
'Healthy' -> 'healthy'.
|
||||||
|
'Uptime %' -> 'uptime'.
|
||||||
|
'RSS' -> 'rss'.
|
||||||
|
'Nitter Version' -> 'version'} asOrderedDictionary.
|
||||||
|
columnNamesMap keysAndValuesDo: [:aName :value |
|
||||||
|
columnedList
|
||||||
|
column: aName
|
||||||
|
text: [:instanceRow | (instanceRow readSlotNamed: value) ifNil: [''] ]
|
||||||
|
].
|
||||||
|
^ columnedList
|
11
Socialmetrica.package/Nitter.class/properties.json
Normal file
11
Socialmetrica.package/Nitter.class/properties.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"commentStamp" : "",
|
||||||
|
"super" : "Object",
|
||||||
|
"category" : "Socialmetrica",
|
||||||
|
"classinstvars" : [ ],
|
||||||
|
"pools" : [ ],
|
||||||
|
"classvars" : [ ],
|
||||||
|
"instvars" : [ ],
|
||||||
|
"name" : "Nitter",
|
||||||
|
"type" : "normal"
|
||||||
|
}
|
7
Socialmetrica.package/NitterInstance.class/README.md
Normal file
7
Socialmetrica.package/NitterInstance.class/README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
I model a Nitter instance uptime & health tracker as described in https://status.d420.de/about.
|
||||||
|
|
||||||
|
Taken from the official documentation, in the previous linkm the fields we are modelling are:
|
||||||
|
|
||||||
|
- healthy: stands for hosts which are reachable and pass a content check.
|
||||||
|
- rss: whether the host has RSS feeds enabled.
|
||||||
|
- version: which nitter version the host reports.
|
@ -0,0 +1,3 @@
|
|||||||
|
accessing
|
||||||
|
hasRSS
|
||||||
|
^ self rss
|
@ -0,0 +1,3 @@
|
|||||||
|
accessing
|
||||||
|
healthy
|
||||||
|
^ healthy
|
@ -0,0 +1,3 @@
|
|||||||
|
accessing
|
||||||
|
isHealthy
|
||||||
|
^ self healthy
|
@ -0,0 +1,5 @@
|
|||||||
|
accessing
|
||||||
|
printOn: aStream
|
||||||
|
super printOn: aStream.
|
||||||
|
^ aStream
|
||||||
|
nextPutAll: '( ', self url ,' | uptime: ', self uptime asString, ' )'
|
@ -0,0 +1,3 @@
|
|||||||
|
accessing
|
||||||
|
rss
|
||||||
|
^ rss
|
@ -0,0 +1,3 @@
|
|||||||
|
accessing
|
||||||
|
uptime
|
||||||
|
^ uptime
|
@ -0,0 +1,3 @@
|
|||||||
|
accessing
|
||||||
|
url
|
||||||
|
^ url
|
17
Socialmetrica.package/NitterInstance.class/properties.json
Normal file
17
Socialmetrica.package/NitterInstance.class/properties.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"commentStamp" : "<historical>",
|
||||||
|
"super" : "Object",
|
||||||
|
"category" : "Socialmetrica",
|
||||||
|
"classinstvars" : [ ],
|
||||||
|
"pools" : [ ],
|
||||||
|
"classvars" : [ ],
|
||||||
|
"instvars" : [
|
||||||
|
"url",
|
||||||
|
"healthy",
|
||||||
|
"uptime",
|
||||||
|
"rss",
|
||||||
|
"version"
|
||||||
|
],
|
||||||
|
"name" : "NitterInstance",
|
||||||
|
"type" : "normal"
|
||||||
|
}
|
@ -3,4 +3,4 @@ nitterProvider
|
|||||||
"For a full list of Nitter providers, see:
|
"For a full list of Nitter providers, see:
|
||||||
|
|
||||||
https://github.com/zedeus/nitter/wiki/Instances"
|
https://github.com/zedeus/nitter/wiki/Instances"
|
||||||
^ 'https://nitter.42l.fr/'
|
^ 'https://nitter.net/'
|
@ -0,0 +1,4 @@
|
|||||||
|
accessing
|
||||||
|
areCommonFilesInstalled
|
||||||
|
"Returns true if common files are in the proper location, and false elsewhere."
|
||||||
|
^ ((TweetsCollection dataStore / 'commons') children collect: [ :file | file basename ] ) includesAll: self class commonFiles keys
|
@ -1,7 +1,29 @@
|
|||||||
accessing
|
accessing
|
||||||
asDictionary
|
asDictionary
|
||||||
|
|
||||||
|
| tweets tweetsHistogramData repliesHistogramData quotesHistogramData retweetsHistogramData |
|
||||||
|
[self config at: 'lang']
|
||||||
|
onErrorDo: [ ^ self inform: 'Please put a lang key with a language (for example: en) in the object config.' ].
|
||||||
|
tweets := self messages.
|
||||||
|
tweetsHistogramData := self tweetsByWeeksTimeSpan.
|
||||||
|
repliesHistogramData := self repliesByWeeksTimeSpan.
|
||||||
|
quotesHistogramData := self quotesReportData.
|
||||||
|
retweetsHistogramData := self retweetsReportData.
|
||||||
^ { 'profile-card-avatar' -> self profileImageFile fullName.
|
^ { 'profile-card-avatar' -> self profileImageFile fullName.
|
||||||
|
'profile-card-avatar-url' -> self profileImageUrl.
|
||||||
'profile-card-fullname' -> self name .
|
'profile-card-fullname' -> self name .
|
||||||
'profile-card-username' -> self userName .
|
'profile-card-username' -> self userName .
|
||||||
'profile-bio' -> self profileBio } asDictionary
|
'profile-bio' -> self profileBio.
|
||||||
|
'messages-size' -> tweets size.
|
||||||
|
'messages-newest' -> tweets newest created asDate greaseString.
|
||||||
|
'messages-oldest' -> tweets oldest created asDate greaseString.
|
||||||
|
'tweets-histogram-labels' -> tweetsHistogramData third.
|
||||||
|
'tweets-histogram-quantity' -> tweetsHistogramData second.
|
||||||
|
'replies-histogram-labels' -> repliesHistogramData third.
|
||||||
|
'replies-histogram-quantity' -> repliesHistogramData second.
|
||||||
|
'retweets-histogram-labels' -> retweetsHistogramData third.
|
||||||
|
'retweets-histogram-quantity' -> retweetsHistogramData second.
|
||||||
|
'quotes-histogram-labels' -> quotesHistogramData third.
|
||||||
|
'quotes-histogram-quantity' -> quotesHistogramData second.
|
||||||
|
'wordcloud-data' -> (self wordcloudDataLanguage: (self config at: 'lang')) first.
|
||||||
|
} asDictionary
|
@ -0,0 +1,28 @@
|
|||||||
|
accessing
|
||||||
|
asDictionaryForWeb
|
||||||
|
|
||||||
|
| tweets tweetsHistogramData repliesHistogramData quotesHistogramData retweetsHistogramData |
|
||||||
|
tweets := self messages.
|
||||||
|
tweetsHistogramData := self tweetsByTimeSpan: 7.
|
||||||
|
repliesHistogramData := self repliesByTimeSpan: 7.
|
||||||
|
quotesHistogramData := self quotesReportData.
|
||||||
|
retweetsHistogramData := self retweetsReportData.
|
||||||
|
|
||||||
|
^ { 'profile-card-avatar' -> self profileImageFile fullName.
|
||||||
|
'profile-card-avatar-url' -> self profileImageUrl.
|
||||||
|
'profile-card-fullname' -> self name .
|
||||||
|
'profile-card-username' -> self userName .
|
||||||
|
'profile-bio' -> self profileBio.
|
||||||
|
'messages-size' -> tweets size.
|
||||||
|
'messages-newest' -> tweets newest created asDate greaseString.
|
||||||
|
'messages-oldest' -> tweets oldest created asDate greaseString.
|
||||||
|
'tweets-histogram-labels' -> tweetsHistogramData third.
|
||||||
|
'tweets-histogram-quantity' -> tweetsHistogramData second.
|
||||||
|
'replies-histogram-labels' -> repliesHistogramData third.
|
||||||
|
'replies-histogram-quantity' -> repliesHistogramData second.
|
||||||
|
'retweets-histogram-labels' -> retweetsHistogramData third.
|
||||||
|
'retweets-histogram-quantity' -> retweetsHistogramData second.
|
||||||
|
'quotes-histogram-labels' -> quotesHistogramData third.
|
||||||
|
'quotes-histogram-quantity' -> quotesHistogramData second.
|
||||||
|
'wordcloud-data' -> self wordcloudData first.
|
||||||
|
} asDictionary
|
16
Socialmetrica.package/NitterUser.class/instance/asTiddler.st
Normal file
16
Socialmetrica.package/NitterUser.class/instance/asTiddler.st
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
accessing
|
||||||
|
asTiddler
|
||||||
|
|
||||||
|
| tempDict tiddler |
|
||||||
|
tiddler := Tiddler new.
|
||||||
|
tiddler customFields.
|
||||||
|
tempDict := self asDictionary.
|
||||||
|
tempDict keysAndValuesDo: [ :key :value |
|
||||||
|
tiddler customFields at: key put: value
|
||||||
|
].
|
||||||
|
^ tiddler
|
||||||
|
created;
|
||||||
|
title: (tiddler customFields at: 'profile-card-username');
|
||||||
|
type: 'text/vnd.tiddlywiki';
|
||||||
|
text:
|
||||||
|
'<<image-card "', self profileImageUrl, '" title:', ($' asString), '<$transclude field="profile-card-fullname"/>', ($' asString), 'text:', ($' asString), '<$transclude field="profile-bio"/>', ($' asString), 'footer:', ($' asString), '@<$transclude field="profile-card-username"/>', ($' asString), 'align:"center" pos:"top">>'.
|
@ -0,0 +1,4 @@
|
|||||||
|
accessing
|
||||||
|
authorIds
|
||||||
|
|
||||||
|
^ TwitterUser storedInstances select: [ :each | each userName = self userName ] thenCollect: [ :each | each id ]
|
@ -0,0 +1,12 @@
|
|||||||
|
accessing
|
||||||
|
avatarPicture
|
||||||
|
| response profileImgFile|
|
||||||
|
profileImgFile := self profileImageFile.
|
||||||
|
profileImgFile exists
|
||||||
|
ifTrue: [ ^ (ImageReadWriter formFromFileNamed: profileImgFile fullName) asElement ].
|
||||||
|
response := ZnClient new url: (self profileImageUrl); get; response.
|
||||||
|
response contentType = ZnMimeType imageJpeg
|
||||||
|
ifTrue: [ ^ (PluginBasedJPEGReadWriter gtFromBuffer: response contents) asElement ].
|
||||||
|
response contentType = ZnMimeType imagePng
|
||||||
|
ifTrue: [ ^ (PNGReadWriter gtFromBuffer: response contents) asElement ].
|
||||||
|
^ GtABContact new avatar
|
@ -0,0 +1,33 @@
|
|||||||
|
accessing
|
||||||
|
collectRawTweetsFrom: anUrl upToPage: anInteger
|
||||||
|
|
||||||
|
| pagesDict response customQuery |
|
||||||
|
pagesDict := self getPagesContentsFrom: anUrl upTo: anInteger.
|
||||||
|
response := TweetsCollection new.
|
||||||
|
customQuery := Dictionary new
|
||||||
|
at: 'parameters' put: pagesDict keys;
|
||||||
|
at: 'date' put: DateAndTime now;
|
||||||
|
yourself.
|
||||||
|
response query: customQuery.
|
||||||
|
pagesDict keysAndValuesDo: [ :key :rawTweets | | temp |
|
||||||
|
temp := (rawTweets xpath: '//div[@class="timeline-item "]') asOrderedCollection
|
||||||
|
collect: [ :xmlElement | xmlElement postCopy ].
|
||||||
|
temp do: [ :tweet | | tempTweet |
|
||||||
|
tempTweet := Tweet new fromNitterHtmlItem: tweet.
|
||||||
|
tempTweet metadata
|
||||||
|
at: DateAndTime now asString put: key;
|
||||||
|
yourself.
|
||||||
|
response add: tempTweet.
|
||||||
|
]
|
||||||
|
].
|
||||||
|
response messages: (response messages select: [ :tweet | tweet isNotNil ]).
|
||||||
|
response messages doWithIndex: [ :tweet :i |
|
||||||
|
| current previous |
|
||||||
|
current := response messages at: i.
|
||||||
|
i < response lastIndex ifTrue: [
|
||||||
|
previous := response messages at: i + 1.
|
||||||
|
current timelines
|
||||||
|
at: self userName put: previous id;
|
||||||
|
yourself ]].
|
||||||
|
^ response.
|
||||||
|
|
@ -0,0 +1,5 @@
|
|||||||
|
accessing
|
||||||
|
collectRawTweetsFromOldestUpToPage: anInteger
|
||||||
|
|
||||||
|
^ self collectRawTweetsFrom: self oldestTweetPageCursor upToPage: anInteger
|
||||||
|
|
@ -0,0 +1,5 @@
|
|||||||
|
accessing
|
||||||
|
collectRawTweetsUpToPage: anInteger
|
||||||
|
|
||||||
|
^ self collectRawTweetsFrom: self userNameLinkWithReplies upToPage: anInteger
|
||||||
|
|
@ -0,0 +1,8 @@
|
|||||||
|
accessing
|
||||||
|
configureDefaultReportingPeriod
|
||||||
|
[ config at: 'reportingPeriod' ]
|
||||||
|
onErrorDo: [ self config
|
||||||
|
at: 'reportingPeriod'
|
||||||
|
put: (Timespan
|
||||||
|
starting: messages oldest created asDateAndTime
|
||||||
|
ending: messages newest created asDateAndTime + 1 minute) ]
|
@ -2,5 +2,7 @@ accessing
|
|||||||
createdAt
|
createdAt
|
||||||
^ createdAt ifNil: [| joinDateString |
|
^ createdAt ifNil: [| joinDateString |
|
||||||
joinDateString := ((self documentTree xpath: '//div[@class="profile-joindate"]/span/@title') stringValue).
|
joinDateString := ((self documentTree xpath: '//div[@class="profile-joindate"]/span/@title') stringValue).
|
||||||
createdAt := (ZTimestampFormat fromString:'4:05 PM - 03 Feb 2001') parse: joinDateString.
|
|
||||||
|
createdAt := (ZTimestampFormat fromString:'4:05 PM - 3 Feb 2001') parse: joinDateString.
|
||||||
|
createdAt := createdAt asDateAndTime
|
||||||
]
|
]
|
@ -1,5 +0,0 @@
|
|||||||
accessing
|
|
||||||
defaultConfig
|
|
||||||
|
|
||||||
self config: { 'folder' -> (FileLocator userData / 'Socialmetrica' / self userName) } asDictionary.
|
|
||||||
^ self config
|
|
@ -1,3 +1,3 @@
|
|||||||
operation
|
operation
|
||||||
documentTree
|
documentTree
|
||||||
^ XMLHTMLParser parse: self userNameLink asUrl retrieveContents
|
^ self documentTreeFor: self userNameLinkWithReplies
|
@ -0,0 +1,3 @@
|
|||||||
|
accessing
|
||||||
|
documentTreeFor: anUrl
|
||||||
|
^ XMLHTMLParser parse:anUrl asUrl retrieveContents
|
@ -1,4 +1,12 @@
|
|||||||
accessing
|
accessing
|
||||||
downloadProfileImage
|
downloadProfileImage
|
||||||
|
|
||||||
^ self exportProfileImageOn: self folder / 'profile-image', 'jpg'
|
self remoteIsFound
|
||||||
|
ifTrue: [ ^ self exportProfileImageOn: self folder / 'profile-image', 'jpg' ]
|
||||||
|
ifFalse: [ | tempFile |
|
||||||
|
tempFile := (self folder / 'profile-image', 'jpg') asFileReference.
|
||||||
|
tempFile ensureCreateFile.
|
||||||
|
tempFile binaryWriteStreamDo: [ :stream |
|
||||||
|
stream nextPutAll: 'https://mutabit.com/repos.fossil/mutabit/uv/wiki/commons/twitter-user-image-default.jpg' asUrl retrieveContents.
|
||||||
|
super class inform: 'Exported as: ', String cr, tempFile fullName.
|
||||||
|
^ self folder / 'profile-image', 'jpg' ]]
|
@ -0,0 +1,40 @@
|
|||||||
|
as yet unclassified
|
||||||
|
downloadWithRenaming: fileReference
|
||||||
|
|
||||||
|
| file tempFile fileHash tempFileHash |
|
||||||
|
file := fileReference asFileReference .
|
||||||
|
tempFile := FileLocator temp / fileReference basename.
|
||||||
|
tempFile ensureCreateFile.
|
||||||
|
tempFile binaryWriteStreamDo: [ :stream |
|
||||||
|
stream nextPutAll: profileImageUrl asUrl retrieveContents.
|
||||||
|
super class inform: 'Exported as: ', String cr, tempFile fullName.
|
||||||
|
].
|
||||||
|
|
||||||
|
OSSUnixSubprocess new
|
||||||
|
command: 'openssl';
|
||||||
|
arguments: { 'dgst' . '-sha256' . file fullName};
|
||||||
|
workingDirectory: (self folder) fullName;
|
||||||
|
redirectStdout;
|
||||||
|
redirectStderr;
|
||||||
|
runAndWaitOnExitDo: [ :process :outString |
|
||||||
|
fileHash := (outString splitOn: ' ') second trimmed].
|
||||||
|
|
||||||
|
OSSUnixSubprocess new
|
||||||
|
command: 'openssl';
|
||||||
|
arguments: { 'dgst' . '-sha256' . tempFile fullName};
|
||||||
|
workingDirectory: (self folder) fullName;
|
||||||
|
redirectStdout;
|
||||||
|
redirectStderr;
|
||||||
|
runAndWaitOnExitDo: [ :process :outString |
|
||||||
|
tempFileHash := (outString splitOn: ' ' ) second trimmed].
|
||||||
|
|
||||||
|
fileHash = tempFileHash
|
||||||
|
ifFalse: [
|
||||||
|
file copyTo: self folder /
|
||||||
|
(file basenameWithoutExtension , '-',
|
||||||
|
('-' join:
|
||||||
|
((file creationTime asLocalStringYMDHM) splitOn: ' ')), '.jpg').
|
||||||
|
file ensureDelete.
|
||||||
|
^ { 'Profile image changed' ->
|
||||||
|
(tempFile moveTo: file)} asDictionary ].
|
||||||
|
^ { 'Same Profile Image' -> file } asDictionary
|
@ -0,0 +1,6 @@
|
|||||||
|
accessing
|
||||||
|
exportDefaultReport
|
||||||
|
|
||||||
|
(self hasFolder: 'templates')
|
||||||
|
ifFalse: [ self installTemplate ].
|
||||||
|
^ self exportWithTemplate: (TweetsCollection dataStore / 'templates' / 'template.mus.tex') into: self folder
|
@ -0,0 +1,9 @@
|
|||||||
|
accessing
|
||||||
|
exportEmptyHistogramNamed: aDictionary
|
||||||
|
|
||||||
|
| histogram |
|
||||||
|
histogram := RSChart new.
|
||||||
|
histogram extent: (aDictionary at: 'extent').
|
||||||
|
histogram build.
|
||||||
|
histogram canvas exportAsFileNamed: (aDictionary at: 'messagesType'), '-histogram' into: self folder.
|
||||||
|
^ self
|
@ -0,0 +1,49 @@
|
|||||||
|
accessing
|
||||||
|
exportHistogramFor: aDictionary By: aTypeString
|
||||||
|
"TODO: quotes and retweets"
|
||||||
|
| messagesDict histogram diagram tempMessages labels subtotals |
|
||||||
|
tempMessages := self perform: (aDictionary at: 'messagesType') asSymbol.
|
||||||
|
|
||||||
|
tempMessages ifEmpty: [ self exportEmptyHistogramNamed:
|
||||||
|
(aDictionary at: 'messagesType'), '-histogram' ].
|
||||||
|
|
||||||
|
((((aDictionary at: 'messagesType') = 'tweets') or: [(aDictionary at: 'messagesType') = 'replies'])
|
||||||
|
and: aTypeString isNumber)
|
||||||
|
ifTrue: [ messagesDict := tempMessages splitBytimeSpansOf: aTypeString ].
|
||||||
|
(aTypeString = 'day' or: [ aTypeString = 'days' ])
|
||||||
|
ifTrue: [ messagesDict := tempMessages splitByDays ].
|
||||||
|
(aTypeString = 'week' or: [ aTypeString = 'weeks' ])
|
||||||
|
ifTrue: [ messagesDict := tempMessages splitByWeeks ].
|
||||||
|
|
||||||
|
(((aDictionary at: 'messagesType') = 'retweets') or: [ (aDictionary at: 'messagesType') = 'quotes' ])
|
||||||
|
ifTrue: [
|
||||||
|
messagesDict := tempMessages asMessagesUserNamesSortedByOccurrences.
|
||||||
|
((aTypeString > messagesDict size) or: [ aTypeString = 1 ])
|
||||||
|
ifFalse: [ | keysToRemove |
|
||||||
|
keysToRemove := OrderedCollection new.
|
||||||
|
1 to: messagesDict size - aTypeString do:
|
||||||
|
[ :i | keysToRemove add: (messagesDict keys at: i + aTypeString) ].
|
||||||
|
messagesDict removeKeys: keysToRemove. ].
|
||||||
|
labels := messagesDict keys.
|
||||||
|
labels := labels collect: [ :profiles | ('@', profiles) ].
|
||||||
|
subtotals := messagesDict values
|
||||||
|
]
|
||||||
|
ifFalse: [ labels := messagesDict keys.
|
||||||
|
subtotals := (messagesDict values collect: [ :collection | collection size ])].
|
||||||
|
|
||||||
|
histogram := RSChart new.
|
||||||
|
histogram extent: (aDictionary at: 'extent').
|
||||||
|
diagram := RSBarPlot new
|
||||||
|
y: subtotals.
|
||||||
|
diagram color: (aDictionary at: 'color').
|
||||||
|
histogram addPlot: diagram.
|
||||||
|
histogram addDecoration: (RSHorizontalTick new
|
||||||
|
fromNames: labels;
|
||||||
|
labelRotation: 0;
|
||||||
|
fontSize: 68 /messagesDict size;
|
||||||
|
yourself).
|
||||||
|
histogram addDecoration: (RSVerticalTick new
|
||||||
|
integer;
|
||||||
|
fontSize: 68 /messagesDict size).
|
||||||
|
histogram build.
|
||||||
|
^ histogram canvas exportAsFileNamed: (aDictionary at: 'messagesType'), '-histogram' into: self folder
|
@ -0,0 +1,11 @@
|
|||||||
|
accessing
|
||||||
|
exportOverviewReportLatex
|
||||||
|
|
||||||
|
| weeks floor divisions |
|
||||||
|
weeks := ((self newestTweet created - self oldestTweet created) days / 7).
|
||||||
|
floor := weeks floor.
|
||||||
|
(weeks - floor) > 0.4
|
||||||
|
ifTrue: [ divisions := floor ]
|
||||||
|
ifFalse: [ divisions := floor + 1 ].
|
||||||
|
self exportOverviewReportLatexWithBars: divisions.
|
||||||
|
^ self folder
|
@ -0,0 +1,11 @@
|
|||||||
|
accessing
|
||||||
|
exportOverviewReportLatexWithBars: anInteger
|
||||||
|
|
||||||
|
self
|
||||||
|
exportDefaultReport;
|
||||||
|
externalWordCloud;
|
||||||
|
exportTweetsHistogramWithBars: anInteger;
|
||||||
|
exportRetweetsHistogramWithBars: anInteger;
|
||||||
|
exportRepliesHistogramWithBars: anInteger;
|
||||||
|
exportQuotesHistogramWithBars: anInteger.
|
||||||
|
^ self folder
|
@ -1,48 +1,13 @@
|
|||||||
accessing
|
accessing
|
||||||
exportProfileImageOn: fileReference
|
exportProfileImageOn: fileReference
|
||||||
|
|
||||||
| file tempFile tempFileHash fileHash |
|
| file |
|
||||||
file := fileReference asFileReference.
|
file := fileReference asFileReference.
|
||||||
file exists
|
file exists ifFalse: [
|
||||||
ifFalse: [ file ensureCreateFile.
|
file ensureCreateFile.
|
||||||
file binaryWriteStreamDo: [ :stream |
|
file binaryWriteStreamDo: [ :stream |
|
||||||
stream nextPutAll: profileImageUrl retrieveContents ].
|
stream nextPutAll: profileImageUrl asUrl retrieveContents ].
|
||||||
super class inform: 'Exported as: ', String cr, file fullName.
|
self class inform: 'Exported as: ', String cr, file fullName.
|
||||||
^ file]
|
^ file
|
||||||
|
].
|
||||||
ifTrue: [
|
self downloadWithRenaming: fileReference
|
||||||
tempFile := FileLocator temp / fileReference basename.
|
|
||||||
tempFile ensureCreateFile.
|
|
||||||
tempFile binaryWriteStreamDo: [ :stream |
|
|
||||||
stream nextPutAll: profileImageUrl retrieveContents.
|
|
||||||
super class
|
|
||||||
inform: 'Exported as: ', String cr, tempFile fullName. ].
|
|
||||||
|
|
||||||
OSSUnixSubprocess new
|
|
||||||
command: 'openssl';
|
|
||||||
arguments: { 'dgst' . '-sha256' . file fullName};
|
|
||||||
workingDirectory: (self folder)fullName;
|
|
||||||
redirectStdout;
|
|
||||||
redirectStderr;
|
|
||||||
runAndWaitOnExitDo: [ :process :outString |
|
|
||||||
fileHash := (outString splitOn: ' ' ) second trimmed].
|
|
||||||
|
|
||||||
OSSUnixSubprocess new
|
|
||||||
command: 'openssl';
|
|
||||||
arguments: { 'dgst' . '-sha256' . tempFile fullName};
|
|
||||||
workingDirectory: (self folder)fullName;
|
|
||||||
redirectStdout;
|
|
||||||
redirectStderr;
|
|
||||||
runAndWaitOnExitDo: [ :process :outString |
|
|
||||||
tempFileHash := (outString splitOn: ' ' ) second trimmed].
|
|
||||||
|
|
||||||
fileHash = tempFileHash
|
|
||||||
ifFalse: [
|
|
||||||
file copyTo: self folder /
|
|
||||||
(file basenameWithoutExtension , '-',
|
|
||||||
('-' join:
|
|
||||||
((file creationTime asLocalStringYMDHM) splitOn: ' ')), '.jpg').
|
|
||||||
file ensureDelete.
|
|
||||||
^ { 'Profile image changed' ->
|
|
||||||
(tempFile moveTo: file)} asDictionary ]].
|
|
||||||
^ { 'Same Profile Image' -> file } asDictionary
|
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
accessing
|
||||||
|
exportQuotesHistogramWithBars: aNumberOfBars
|
||||||
|
|
||||||
|
| quotesDict |
|
||||||
|
quotesDict := {
|
||||||
|
'messagesType' -> 'quotes'.
|
||||||
|
'extent' -> (800@200).
|
||||||
|
'color' -> (Color r:(89/255) g:(217/255) b:(95/255))
|
||||||
|
} asDictionary.
|
||||||
|
^ self exportHistogramFor: quotesDict By: aNumberOfBars
|
||||||
|
|
||||||
|
|
||||||
|
"| keysToRemove quotes labels quotesHistogram diagram |
|
||||||
|
quotes := self quotes asMessagesUserNamesSortedByOccurrences.
|
||||||
|
(aNumberOfBars > quotes size) ifTrue: [ ^ self exportQuotesHistogram ].
|
||||||
|
keysToRemove := OrderedCollection new.
|
||||||
|
1 to: quotes size - aNumberOfBars do:
|
||||||
|
[ :i | keysToRemove add: (quotes keys at: i + aNumberOfBars) ].
|
||||||
|
quotes removeKeys: keysToRemove.
|
||||||
|
|
||||||
|
labels := quotes keys.
|
||||||
|
labels := labels collect: [ :profiles | ('@', profiles) ].
|
||||||
|
quotesHistogram := RSChart new.
|
||||||
|
quotesHistogram extent: 800@200.
|
||||||
|
diagram := RSBarPlot new
|
||||||
|
y: quotes values.
|
||||||
|
diagram color: (Color r:(89/255) g:(217/255) b:(95/255)).
|
||||||
|
quotesHistogram addPlot: diagram.
|
||||||
|
quotesHistogram addDecoration: (RSHorizontalTick new
|
||||||
|
fromNames: labels;
|
||||||
|
labelRotation: 0;
|
||||||
|
fontSize: 72 /quotes size;
|
||||||
|
yourself).
|
||||||
|
quotesHistogram addDecoration: (RSVerticalTick new
|
||||||
|
asFloat: 2;
|
||||||
|
fontSize: 72 /quotes size).
|
||||||
|
quotesHistogram build.
|
||||||
|
|
||||||
|
quotesHistogram canvas pdfExporter
|
||||||
|
zoomToShapes;
|
||||||
|
noFixedShapes;
|
||||||
|
fileName: (self folder / 'quotes-histogram.pdf')fullName;
|
||||||
|
export.
|
||||||
|
quotesHistogram canvas pngExporter
|
||||||
|
zoomToShapes;
|
||||||
|
noFixedShapes;
|
||||||
|
fileName: (self folder / 'quotes-histogram.png')fullName;
|
||||||
|
export.
|
||||||
|
^ self folder / 'quotes-histogram.png'"
|
@ -0,0 +1,10 @@
|
|||||||
|
accessing
|
||||||
|
exportRepliesHistogramWithBars: aNumberOfBars
|
||||||
|
|
||||||
|
| repliesDict |
|
||||||
|
repliesDict := {
|
||||||
|
'messagesType' -> 'replies'.
|
||||||
|
'extent' -> (800@200).
|
||||||
|
'color' -> (Color r:(246/255) g:(185/255) b:(46/255))
|
||||||
|
} asDictionary.
|
||||||
|
^ self exportHistogramFor: repliesDict By: aNumberOfBars
|
@ -0,0 +1,10 @@
|
|||||||
|
accessing
|
||||||
|
exportRetweetsHistogramWithBars: aNumberOfBars
|
||||||
|
|
||||||
|
| retweetsDict |
|
||||||
|
retweetsDict := {
|
||||||
|
'messagesType' -> 'retweets'.
|
||||||
|
'extent' -> (800@200).
|
||||||
|
'color' -> (Color r:(217/255) g:(56/255) b: (124/255))
|
||||||
|
} asDictionary.
|
||||||
|
^ self exportHistogramFor: retweetsDict By: aNumberOfBars
|
@ -0,0 +1,8 @@
|
|||||||
|
accessing
|
||||||
|
exportStaticWebReport
|
||||||
|
|
||||||
|
(self hasFolder: 'commons')
|
||||||
|
ifFalse: [ self installCommons ].
|
||||||
|
(self hasFolder: 'templates')
|
||||||
|
ifFalse: [ self installTemplate ].
|
||||||
|
^ self exportWithTemplate: (TweetsCollection dataStore / 'templates' / 'index.mus.html') into: self folder
|
@ -0,0 +1,10 @@
|
|||||||
|
accessing
|
||||||
|
exportTweetsHistogramWithBars: aNumberOfBars
|
||||||
|
|
||||||
|
| tweetsDict |
|
||||||
|
tweetsDict := {
|
||||||
|
'messagesType' -> 'tweets'.
|
||||||
|
'extent' -> (800@200).
|
||||||
|
'color' -> (Color r:(91/255) g:(131/255) b:(222/255))
|
||||||
|
} asDictionary.
|
||||||
|
^ self exportHistogramFor: tweetsDict By: aNumberOfBars
|
@ -0,0 +1,11 @@
|
|||||||
|
accessing
|
||||||
|
exportWeekReportLatexBeginningAt: aDay
|
||||||
|
|
||||||
|
self config
|
||||||
|
at: 'reportingPeriod'
|
||||||
|
put: (Timespan starting: aDay asDateAndTime ending: ( aDay asDateAndTime + 7 days)).
|
||||||
|
self messages: self messages.
|
||||||
|
self
|
||||||
|
exportDefaultReport;
|
||||||
|
externalWordCloud.
|
||||||
|
^ self folder
|
@ -1,12 +1,29 @@
|
|||||||
accessing
|
accessing
|
||||||
exportWithTemplate: mustacheFile into: folder
|
exportWithTemplate: mustacheFile into: folder
|
||||||
|
|
||||||
| tempDictionary modified |
|
| tempDictionary bioModified userModified nameModified |
|
||||||
tempDictionary := self asDictionary copy.
|
tempDictionary := self asDictionary copy.
|
||||||
modified := self asDictionary at: 'profile-bio'.
|
(mustacheFile fullName endsWith: '.html')
|
||||||
modified := modified copyReplaceAll: '@' with: '\@'.
|
ifTrue: [ ^ MarkupFile
|
||||||
modified := modified copyReplaceAll: '_' with: '\_'.
|
exportAsFileOn: (folder / self userName , 'html')
|
||||||
tempDictionary at: 'profile-bio' put: modified.
|
containing:(mustacheFile asMustacheTemplate value: tempDictionary)].
|
||||||
MarkupFile
|
|
||||||
|
(mustacheFile fullName endsWith: '.tex')
|
||||||
|
ifTrue: [ bioModified := self asDictionary at: 'profile-bio'.
|
||||||
|
bioModified := bioModified copyReplaceAll: '@' with: '\@'.
|
||||||
|
bioModified := bioModified copyReplaceAll: '_' with: '\_'.
|
||||||
|
bioModified := bioModified copyReplaceAll: '#' with: '\#'.
|
||||||
|
bioModified := bioModified copyReplaceAll: '👑😎' with: ''.
|
||||||
|
bioModified := bioModified copyReplaceAll: '🇨🇴' with: ''.
|
||||||
|
bioModified := bioModified copyReplaceAll: '|' with: ''.
|
||||||
|
userModified := self asDictionary at: 'profile-card-username'.
|
||||||
|
userModified := userModified copyReplaceAll: '_' with: '\_'.
|
||||||
|
nameModified := self asDictionary at: 'profile-card-fullname'.
|
||||||
|
nameModified := nameModified copyReplaceAll: '🇨🇴' with: ''.
|
||||||
|
tempDictionary at: 'profile-bio' put: bioModified;
|
||||||
|
at: 'profile-card-username' put: userModified;
|
||||||
|
at: 'profile-card-fullname' put: nameModified.
|
||||||
|
^ MarkupFile
|
||||||
exportAsFileOn: (folder / self userName , 'tex')
|
exportAsFileOn: (folder / self userName , 'tex')
|
||||||
containing:(mustacheFile asMustacheTemplate value: tempDictionary)
|
containing:(mustacheFile asMustacheTemplate value: tempDictionary)]
|
||||||
|
|
@ -1,9 +1,13 @@
|
|||||||
accessing
|
accessing
|
||||||
externalWordCloud
|
externalWordCloud
|
||||||
|
"TO DO: refactor with externalWordCloudWithLanguage:"
|
||||||
|
|
||||||
| text outputFile |
|
| text outputFile |
|
||||||
outputFile := (self folder / 'nube.png')fullName.
|
self areCommonFilesInstalled
|
||||||
text := (self folder / self userName, 'words', 'txt')fullName.
|
ifFalse: [ self installCommons ].
|
||||||
|
self writeWordsFile.
|
||||||
|
outputFile := (self folder / 'wordcloud.png') fullName.
|
||||||
|
text := (self folder / 'words', 'txt') fullName.
|
||||||
OSSUnixSubprocess new
|
OSSUnixSubprocess new
|
||||||
command: 'wordcloud_cli';
|
command: 'wordcloud_cli';
|
||||||
arguments: { '--text' . text .
|
arguments: { '--text' . text .
|
||||||
@ -13,8 +17,9 @@ externalWordCloud
|
|||||||
'--height' . '357' .
|
'--height' . '357' .
|
||||||
'--background' . 'white' .
|
'--background' . 'white' .
|
||||||
'--mode' . 'RGBA' .
|
'--mode' . 'RGBA' .
|
||||||
'--stopwords' . '../commons/stopwords-es.txt' .
|
'--stopwords' . '../../../commons/stopwords-es.txt'.
|
||||||
'--mask' . '../commons/nube-mascara.jpg'};
|
"'--mask' . '../../../commons/nube-mascara.jpg'"
|
||||||
|
};
|
||||||
workingDirectory: self folder fullName;
|
workingDirectory: self folder fullName;
|
||||||
redirectStdout;
|
redirectStdout;
|
||||||
redirectStderr;
|
redirectStderr;
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
accessing
|
||||||
|
externalWordCloudWithLanguage: language
|
||||||
|
"I proces and render a wordcloud image without stopwords in: es and en."
|
||||||
|
|
||||||
|
| text outputFile |
|
||||||
|
self areCommonFilesInstalled
|
||||||
|
ifFalse: [ self installCommons ].
|
||||||
|
self writeWordsFile.
|
||||||
|
outputFile := (self folder / 'wordcloud.png') fullName.
|
||||||
|
text := (self folder / 'words', 'txt') fullName.
|
||||||
|
OSSUnixSubprocess new
|
||||||
|
command: 'wordcloud_cli';
|
||||||
|
arguments: { '--text' . text .
|
||||||
|
'--imagefile' . outputFile .
|
||||||
|
'--color' . '#5B83DE' .
|
||||||
|
'--width' . '1153' .
|
||||||
|
'--height' . '357' .
|
||||||
|
'--background' . 'white' .
|
||||||
|
'--mode' . 'RGBA' .
|
||||||
|
'--stopwords' . '../../../commons/stopwords-', language, '.txt'.
|
||||||
|
"'--mask' . '../../../commons/nube-mascara.jpg'"
|
||||||
|
};
|
||||||
|
workingDirectory: self folder fullName;
|
||||||
|
redirectStdout;
|
||||||
|
redirectStderr;
|
||||||
|
runAndWaitOnExitDo: [ :process :outString | ^ outputFile asFileReference ]
|
@ -0,0 +1,14 @@
|
|||||||
|
accessing
|
||||||
|
getLocalMessages
|
||||||
|
|
||||||
|
| allTweets myTweets tweetsWithAntecesor |
|
||||||
|
|
||||||
|
TweetsCollection storeDB.
|
||||||
|
allTweets := Tweet storedInstances asOrderedCollection.
|
||||||
|
allTweets ifNil: [ ^ nil ].
|
||||||
|
myTweets := TweetsCollection new.
|
||||||
|
tweetsWithAntecesor := allTweets select: [ :each | each timelines isNotEmpty and: [ each timelines keys first = self userName ]].
|
||||||
|
myTweets messages: tweetsWithAntecesor.
|
||||||
|
self messages: myTweets.
|
||||||
|
^ self
|
||||||
|
|
@ -1,24 +1,5 @@
|
|||||||
accessing
|
accessing
|
||||||
getMessages
|
getMessages
|
||||||
| lastTweetsRaw customQuery lastTweets |
|
|
||||||
lastTweetsRaw := self rssFeed xmlDocument xpath: '//item'.
|
|
||||||
lastTweets := TweetsCollection new.
|
|
||||||
customQuery := Dictionary new
|
|
||||||
at: 'parameters' put: self userNameLink;
|
|
||||||
at: 'date' put: DateAndTime now;
|
|
||||||
yourself.
|
|
||||||
lastTweets query: customQuery.
|
|
||||||
lastTweetsRaw doWithIndex: [ :rssTweet :i | | current previous |
|
|
||||||
current := Tweet new fromNitterRssItem: rssTweet.
|
|
||||||
i < lastTweetsRaw size ifTrue: [
|
|
||||||
previous := Tweet new fromNitterRssItem: (lastTweetsRaw at: i + 1).
|
|
||||||
current timelines
|
|
||||||
at: self userName put: previous id;
|
|
||||||
yourself.
|
|
||||||
].
|
|
||||||
current queries add: customQuery.
|
|
||||||
lastTweets add: current.
|
|
||||||
].
|
|
||||||
self tweets: lastTweets.
|
|
||||||
^ self tweets
|
|
||||||
|
|
||||||
|
self getLocalMessages ifNil: [ self getRemoteMessagesFromHtml ].
|
||||||
|
^ self messages
|
@ -0,0 +1,19 @@
|
|||||||
|
accessing
|
||||||
|
getPagesContentsFrom: anURL upTo: anInteger
|
||||||
|
"I retroactively get all pages contents until a specified page number.
|
||||||
|
|
||||||
|
TO DO: should this be splitted back to two methods, one getting the page urls and other its content?
|
||||||
|
or do we always be getting the cursor urls and its contents all the time.
|
||||||
|
[ ] Benchmark alternative approaches."
|
||||||
|
| response nextPageLink previousPageLink |
|
||||||
|
|
||||||
|
response := OrderedDictionary new.
|
||||||
|
response at: anURL put: (self documentTreeFor: anURL).
|
||||||
|
previousPageLink := anURL.
|
||||||
|
anInteger - 1 timesRepeat: [ | pageCursor |
|
||||||
|
pageCursor := self pageCursorFor:previousPageLink.
|
||||||
|
nextPageLink := self userNameLink, '/with_replies', pageCursor keys first.
|
||||||
|
response at: nextPageLink put: (XMLHTMLParser parse:nextPageLink asUrl retrieveContents).
|
||||||
|
previousPageLink := nextPageLink
|
||||||
|
].
|
||||||
|
^ response
|
@ -0,0 +1,4 @@
|
|||||||
|
accessing
|
||||||
|
getPagesContentsFromOldestUpto: anInteger
|
||||||
|
|
||||||
|
^ self getPagesContentsFrom: ((self oldestTweet metadata select: [ :item | item isString and: [ item beginsWith: 'https://' ]]) values first) upTo: anInteger
|
@ -0,0 +1,8 @@
|
|||||||
|
accessing
|
||||||
|
getPagesContentsUpto: anInteger
|
||||||
|
"I retroactively get all pages contents until a specified page number.
|
||||||
|
|
||||||
|
TO DO: should this be splitted back to two methods, one getting the page urls and other its content?
|
||||||
|
or do we always be getting the cursor urls and its contents all the time.
|
||||||
|
[ ] Benchmark alternative approaches."
|
||||||
|
^ self getPagesContentsFrom: self userNameLinkWithReplies upTo: anInteger
|
@ -0,0 +1,4 @@
|
|||||||
|
accessing
|
||||||
|
getRemoteMessagesFromHtml
|
||||||
|
|
||||||
|
^ messages := self collectRawTweetsUpToPage: 1
|
@ -0,0 +1,6 @@
|
|||||||
|
accessing
|
||||||
|
hasFolder: folderName
|
||||||
|
|
||||||
|
| fullFolderPath |
|
||||||
|
fullFolderPath :=(TweetsCollection dataStore / folderName ).
|
||||||
|
^ fullFolderPath exists and: [ fullFolderPath children isNotEmpty ]
|
@ -1,3 +1,7 @@
|
|||||||
accessing
|
accessing
|
||||||
id
|
id
|
||||||
^ id ifNil: [ id := (self profileImageUrl asUrl segments select: [ :each | each isAllDigits ]) first.]
|
^ id ifNil: [
|
||||||
|
self profileImageUrl
|
||||||
|
ifNil: [ id := 0 ]
|
||||||
|
ifNotNil: [ id := (self profileImageUrl asUrl segments select: [ :each | each isAllDigits ]) first. ]
|
||||||
|
]
|
@ -0,0 +1,22 @@
|
|||||||
|
accessing
|
||||||
|
installCommons
|
||||||
|
|
||||||
|
| commonFiles folder |
|
||||||
|
commonFiles := #(
|
||||||
|
'https://mutabit.com/repos.fossil/mutabit/raw?name=wiki/commons/stopwords-es.txt&ci=tip'
|
||||||
|
'https://mutabit.com/repos.fossil/mutabit/raw?name=wiki/commons/stopwords-en.txt&ci=tip'
|
||||||
|
'https://mutabit.com/repos.fossil/mutabit/uv/wiki/commons/nube-mascara.jpg'
|
||||||
|
'https://mutabit.com/repos.fossil/mutabit/uv/wiki/commons/logo-mutabit-negro.png').
|
||||||
|
folder := TweetsCollection dataStore / 'commons'.
|
||||||
|
folder exists
|
||||||
|
ifTrue: [ folder ensureDeleteAllChildren ]
|
||||||
|
ifFalse: [ folder ensureCreateDirectory].
|
||||||
|
commonFiles do: [ :fileUrl | | temp |
|
||||||
|
ZnClient new
|
||||||
|
url: fileUrl;
|
||||||
|
downloadTo: folder.
|
||||||
|
temp := (folder children select: [ :file | file basename includesSubstring: 'raw' ]).
|
||||||
|
temp isNotEmpty ifTrue: [
|
||||||
|
temp first renameTo: (((fileUrl splitOn: 'raw?') second splitOn: '/') last removeSuffix: '&ci=tip')].
|
||||||
|
].
|
||||||
|
^ folder
|
@ -0,0 +1,22 @@
|
|||||||
|
accessing
|
||||||
|
installTemplate
|
||||||
|
|
||||||
|
| templateFiles folder |
|
||||||
|
templateFiles := #(
|
||||||
|
'https://mutabit.com/repos.fossil/mutabit/raw?name=plantillas/TwentySecondsCV/twentysecondcvMod.cls&ci=tip'
|
||||||
|
'https://mutabit.com/repos.fossil/mutabit/raw?name=plantillas/TwentySecondsCV/template.mus.tex&ci=tip'
|
||||||
|
'https://mutabit.com/repos.fossil/mutabit/raw?name=plantillas/SarissaPersonalBlog/index.mus.html&ci=tip'
|
||||||
|
'https://mutabit.com/repos.fossil/mutabit/raw?name=plantillas/SarissaPersonalBlog/output.css&ci=tip'
|
||||||
|
'https://mutabit.com/repos.fossil/mutabit/raw?name=plantillas/SarissaPersonalBlog/echarts-wordcloud.min.js&ci=tip').
|
||||||
|
folder := TweetsCollection dataStore / 'templates'.
|
||||||
|
folder exists
|
||||||
|
ifTrue: [ folder ensureDeleteAllChildren ]
|
||||||
|
ifFalse: [ folder ensureCreateDirectory].
|
||||||
|
templateFiles do: [ :fileUrl |
|
||||||
|
ZnClient new
|
||||||
|
url: fileUrl;
|
||||||
|
downloadTo: folder.
|
||||||
|
(folder children detect: [ :file | file basename includesSubstring: 'raw' ])
|
||||||
|
renameTo: (((fileUrl splitOn: 'raw?') second splitOn: '/') last removeSuffix: '&ci=tip')
|
||||||
|
].
|
||||||
|
^ folder
|
@ -0,0 +1,5 @@
|
|||||||
|
accessing
|
||||||
|
lastTweetsFromHtml
|
||||||
|
|
||||||
|
^ (self documentTree xpath: '//div[@class="timeline-item "]')
|
||||||
|
asOrderedCollection collect: [ :xmlElement | xmlElement postCopy ]
|
12
Socialmetrica.package/NitterUser.class/instance/messages.st
Normal file
12
Socialmetrica.package/NitterUser.class/instance/messages.st
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
accessing
|
||||||
|
messages
|
||||||
|
messages ifNil: [ messages := TweetsCollection new ].
|
||||||
|
messages ifEmpty: [ self getLocalMessages ].
|
||||||
|
messages ifEmpty: [ self getRemoteMessagesFromHtml ].
|
||||||
|
config at: 'reportingPeriod' ifAbsent: [ ^ messages ].
|
||||||
|
"self configureDefaultReportingPeriod."
|
||||||
|
^ messages
|
||||||
|
select: [ :message |
|
||||||
|
message created
|
||||||
|
between: self reportingPeriod start
|
||||||
|
and: self reportingPeriod end ]
|
@ -1,3 +1,6 @@
|
|||||||
accessing
|
accessing
|
||||||
name
|
name
|
||||||
^ name ifNil: [ name := ((self rssFeed requiredItems title) splitOn: '/') first ]
|
|
||||||
|
| documentTree |
|
||||||
|
documentTree := [ self documentTree ] onErrorDo: [ ^ nil ].
|
||||||
|
^ name ifNil: [ name := (documentTree xpath: '//div[@class="profile-card-tabs-name"]//a[@class="profile-card-fullname"]') stringValue ]
|
@ -0,0 +1,4 @@
|
|||||||
|
accessing
|
||||||
|
newestTweet
|
||||||
|
|
||||||
|
^ (self tweets select: [ :tweet | tweet created = ((self tweets collect: [ :each | each created ]) asSortedCollection last)]) first.
|
@ -1,18 +0,0 @@
|
|||||||
accessing
|
|
||||||
numberOfURLsForLoadingTweets: number
|
|
||||||
|
|
||||||
| collectionURLs count asURLs |
|
|
||||||
collectionURLs := {
|
|
||||||
self userNameLink .
|
|
||||||
(self userNameLink, ((self documentTree xPath: '//a[.="Load more"]') @ 'href') stringValue) .} asOrderedCollection.
|
|
||||||
|
|
||||||
number <= 2 ifTrue: [ ^ collectionURLs ].
|
|
||||||
count := 2.
|
|
||||||
(number-count) timesRepeat: [ | tempDoc |
|
|
||||||
tempDoc := XMLHTMLParser parse: (collectionURLs at: count) asUrl retrieveContents.
|
|
||||||
collectionURLs
|
|
||||||
add: (self userNameLink,
|
|
||||||
((tempDoc xPath: '//a[.="Load more"]') @ 'href') stringValue).
|
|
||||||
count := count+1 ].
|
|
||||||
asURLs := collectionURLs collect: [ :string | string asUrl ].
|
|
||||||
^ asURLs.
|
|
@ -0,0 +1,4 @@
|
|||||||
|
accessing
|
||||||
|
oldestTweet
|
||||||
|
|
||||||
|
^ (self tweets select: [ :tweet | tweet created = ((self tweets collect: [ :each | each created ]) asSortedCollection first)]) first.
|
@ -0,0 +1,3 @@
|
|||||||
|
accessing
|
||||||
|
oldestTweetPageCursor
|
||||||
|
^ (self oldestTweet metadata select: [ :item | item isString and: [ item beginsWith: 'https://' ]]) values first value
|
@ -0,0 +1,10 @@
|
|||||||
|
accessing
|
||||||
|
pageCursorFor: anUrl
|
||||||
|
|
||||||
|
| response value key |
|
||||||
|
response := Dictionary new.
|
||||||
|
value := self documentTreeFor: anUrl.
|
||||||
|
key := ((value xpath: '//a[.="Load more"]') @ 'href')stringValue.
|
||||||
|
^ response
|
||||||
|
at: key put: value;
|
||||||
|
yourself
|
@ -0,0 +1,14 @@
|
|||||||
|
accessing
|
||||||
|
pageDocTrees: anInteger
|
||||||
|
|
||||||
|
| response nextPageLink previousPageLink |
|
||||||
|
|
||||||
|
response := OrderedDictionary new.
|
||||||
|
previousPageLink := self userNameLink.
|
||||||
|
response add: previousPageLink.
|
||||||
|
anInteger - 1 timesRepeat: [
|
||||||
|
nextPageLink := self userNameLink, (self pageCursorFor:previousPageLink) value.
|
||||||
|
response add: nextPageLink.
|
||||||
|
previousPageLink := nextPageLink
|
||||||
|
].
|
||||||
|
^ response
|
@ -2,6 +2,8 @@ accessing
|
|||||||
profileImageFile
|
profileImageFile
|
||||||
|
|
||||||
| file |
|
| file |
|
||||||
file := (self folder / self userName, 'jpg').
|
file := (self folder / 'profile-image', 'jpg').
|
||||||
file exists ifTrue: [ ^ file ].
|
file exists ifTrue: [ file asFileReference size = 0
|
||||||
|
ifTrue:[ ^ self downloadProfileImage ].
|
||||||
|
^ file. ].
|
||||||
^ self downloadProfileImage
|
^ self downloadProfileImage
|
@ -1,4 +1,8 @@
|
|||||||
accessing
|
accessing
|
||||||
profileImageUrl
|
profileImageUrl
|
||||||
^ profileImageUrl ifNil: [
|
|
||||||
profileImageUrl := ((self rssFeed xmlDocument xpath: '//image/url') stringValue copyReplaceAll: '%2F' with: '/') ]
|
| documentTree |
|
||||||
|
self remoteIsFound
|
||||||
|
ifFalse:[ ^ nil ].
|
||||||
|
documentTree := self documentTree.
|
||||||
|
^ profileImageUrl := self class nitterProvider, (((documentTree xpath: '//div[@class="profile-card-info"]//a[@class="profile-card-avatar"]') @ 'href') stringValue copyReplaceAll: '%2F' with: '/') copyWithoutFirst
|
@ -0,0 +1,9 @@
|
|||||||
|
accessing
|
||||||
|
quotes
|
||||||
|
|
||||||
|
self messages ifEmpty: [ self getMessages ].
|
||||||
|
^ TweetsCollection new
|
||||||
|
messages: (self messages
|
||||||
|
select: [ :each |
|
||||||
|
(each metadata at: 'quote') isNotEmpty and: [ each user userName = self userName] ]);
|
||||||
|
yourself
|
@ -0,0 +1,22 @@
|
|||||||
|
accessing
|
||||||
|
quotesReportData
|
||||||
|
|
||||||
|
| tempDict labels xAxis |
|
||||||
|
self quotes isEmpty
|
||||||
|
ifTrue: [ ^ { OrderedDictionary new.
|
||||||
|
('[''', '0', ''']').
|
||||||
|
('[''', 'No quotes', ''']')} ].
|
||||||
|
tempDict := self quotes asMessagesUserNamesSortedByOccurrences.
|
||||||
|
|
||||||
|
tempDict size > 10 ifTrue: [
|
||||||
|
tempDict := (tempDict associations copyFrom: 1 to: 10) asOrderedDictionary ].
|
||||||
|
|
||||||
|
labels := tempDict keys.
|
||||||
|
labels := labels collect: [ :profile | ($' asString), '@', profile, ($' asString) ].
|
||||||
|
xAxis := tempDict values.
|
||||||
|
xAxis := xAxis collect: [ :value | ($' asString), (value asString), ($' asString) ].
|
||||||
|
^ {
|
||||||
|
tempDict.
|
||||||
|
('[', (',' join: xAxis), ']').
|
||||||
|
('[', (',' join: labels), ']').
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
accessing
|
||||||
|
refreshProfileImageUrl
|
||||||
|
self profileImageUrl: nil.
|
||||||
|
self profileImageUrl
|
@ -0,0 +1,5 @@
|
|||||||
|
accessing
|
||||||
|
remoteIsFound
|
||||||
|
|
||||||
|
^ (ZnClient new url: (self userNameLink asUrl); get; response) isNotFound not
|
||||||
|
|
@ -0,0 +1,7 @@
|
|||||||
|
accessing
|
||||||
|
replies
|
||||||
|
|
||||||
|
self messages ifEmpty: [ self getMessages ].
|
||||||
|
^ TweetsCollection new
|
||||||
|
messages: (self tweets select: [ :each | (each metadata at: 'replie to') isNotEmpty]);
|
||||||
|
yourself
|
@ -0,0 +1,19 @@
|
|||||||
|
accessing
|
||||||
|
repliesByTimeSpan: divisions
|
||||||
|
|
||||||
|
| tweetsByTimeSpan xAxis labels |
|
||||||
|
tweetsByTimeSpan := self collectMessages: [ self replies ] byTimeSpanSplits: divisions.
|
||||||
|
xAxis := OrderedCollection new.
|
||||||
|
(tweetsByTimeSpan values collect: [ :collection | collection size ]) do: [ :number |
|
||||||
|
xAxis add: ($' asString), (number asString), ($' asString)
|
||||||
|
].
|
||||||
|
labels := OrderedCollection new.
|
||||||
|
tweetsByTimeSpan keys do: [ :string |
|
||||||
|
labels add: ($' asString), string, ($' asString)
|
||||||
|
].
|
||||||
|
^ {
|
||||||
|
tweetsByTimeSpan.
|
||||||
|
('[', (',' join: xAxis), ']').
|
||||||
|
('[', (',' join: labels), ']').
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
|||||||
|
accessing
|
||||||
|
repliesByWeeksTimeSpan
|
||||||
|
|
||||||
|
| tweetsByTimeSpan xAxis labels |
|
||||||
|
self replies isEmpty
|
||||||
|
ifTrue: [ ^ { OrderedDictionary new.
|
||||||
|
('[''', '0', ''']').
|
||||||
|
('[''', 'No replies', ''']')} ].
|
||||||
|
tweetsByTimeSpan := self collectMessages: [ self replies ] byTimeSpanSplits: self tweetsDivisionsByWeeks.
|
||||||
|
xAxis := OrderedCollection new.
|
||||||
|
(tweetsByTimeSpan values collect: [ :collection | collection size ]) do: [ :number |
|
||||||
|
xAxis add: ($' asString), (number asString), ($' asString)
|
||||||
|
].
|
||||||
|
labels := OrderedCollection new.
|
||||||
|
tweetsByTimeSpan keys do: [ :string |
|
||||||
|
labels add: ($' asString), string, ($' asString)
|
||||||
|
].
|
||||||
|
^ {
|
||||||
|
tweetsByTimeSpan.
|
||||||
|
('[', (',' join: xAxis), ']').
|
||||||
|
('[', (',' join: labels), ']').
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
|||||||
|
private
|
||||||
|
repliesFromImage
|
||||||
|
"Used for testing external classes, disposable"
|
||||||
|
^ self tweetsFromImage select: [ :tweet | (tweet metadata at: 'replie to') isNotEmpty]
|
@ -0,0 +1,5 @@
|
|||||||
|
accessing
|
||||||
|
reportingPeriod
|
||||||
|
^ self config
|
||||||
|
at: 'reportingPeriod'
|
||||||
|
ifAbsentPut: [ { self messages oldest created . self messages newest created } ]
|
@ -1,6 +1,7 @@
|
|||||||
accessing
|
accessing
|
||||||
retrieveContents
|
retrieveContents
|
||||||
self userName ifNil: [^ self].
|
self userName ifNil: [^ self].
|
||||||
|
" self retrieveLocalContents ifNotNil: [ ^ self ]."
|
||||||
^ self
|
^ self
|
||||||
id;
|
id;
|
||||||
name;
|
name;
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
accessing
|
||||||
|
retrieveLocalContents
|
||||||
|
| profileTemp |
|
||||||
|
profileTemp := self class stored detect: [ :each | each userName = self userName ].
|
||||||
|
profileTemp getLocalMessages.
|
||||||
|
^ profileTemp
|
@ -0,0 +1,7 @@
|
|||||||
|
accessing
|
||||||
|
retweets
|
||||||
|
|
||||||
|
self messages ifEmpty: [ self getMessages ].
|
||||||
|
^ TweetsCollection new
|
||||||
|
messages: (self messages select: [ :each | each authorId ~= self id]);
|
||||||
|
yourself
|
@ -0,0 +1,18 @@
|
|||||||
|
accessing
|
||||||
|
retweetsReportData
|
||||||
|
|
||||||
|
| tempDict labels xAxis |
|
||||||
|
tempDict := self retweets asMessagesUserNamesSortedByOccurrences.
|
||||||
|
|
||||||
|
tempDict size > 10 ifTrue: [
|
||||||
|
tempDict := (tempDict associations copyFrom: 1 to: 10) asOrderedDictionary ].
|
||||||
|
|
||||||
|
labels := tempDict keys.
|
||||||
|
labels := labels collect: [ :profile | ($' asString), '@', profile, ($' asString) ].
|
||||||
|
xAxis := tempDict values.
|
||||||
|
xAxis := xAxis collect: [ :value | ($' asString), (value asString), ($' asString) ].
|
||||||
|
^ {
|
||||||
|
tempDict.
|
||||||
|
('[', (',' join: xAxis), ']').
|
||||||
|
('[', (',' join: labels), ']').
|
||||||
|
}
|
@ -1,4 +0,0 @@
|
|||||||
accessing
|
|
||||||
rssFeed
|
|
||||||
|
|
||||||
^ RSSTools createRSSFeedFor: self userNameLink , '/rss'
|
|
@ -4,7 +4,7 @@ storeContents
|
|||||||
| objectString directory tempFile oldFile dehidratated |
|
| objectString directory tempFile oldFile dehidratated |
|
||||||
|
|
||||||
dehidratated := self copy.
|
dehidratated := self copy.
|
||||||
dehidratated tweets: nil.
|
dehidratated messages: nil.
|
||||||
objectString := STON toStringPretty: dehidratated.
|
objectString := STON toStringPretty: dehidratated.
|
||||||
directory := self folder ensureCreateDirectory.
|
directory := self folder ensureCreateDirectory.
|
||||||
oldFile := directory / 'profile', 'ston'.
|
oldFile := directory / 'profile', 'ston'.
|
||||||
|
14
Socialmetrica.package/NitterUser.class/instance/tweets.st
Normal file
14
Socialmetrica.package/NitterUser.class/instance/tweets.st
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
accessing
|
||||||
|
tweets
|
||||||
|
| subcollection |
|
||||||
|
self messages ifEmpty: [ self getMessages ].
|
||||||
|
"TO DO: It seems that Twitter changes the user id, which we thought was unchangeable. This deals with the detected changes so far."
|
||||||
|
|
||||||
|
subcollection := self messages
|
||||||
|
select: [ :each | self authorIds includes: each authorId ].
|
||||||
|
|
||||||
|
(#('FranciaMarquezM' 'sandralajas' 'IBetancourtCol' 'sergio_fajardo' 'ingrodolfohdez' 'CastilloMarelen') includes: self userName)
|
||||||
|
ifFalse: [ subcollection := self messages select: [ :each | each authorId = self id ]].
|
||||||
|
^ TweetsCollection new
|
||||||
|
messages: subcollection;
|
||||||
|
yourself
|
@ -0,0 +1,19 @@
|
|||||||
|
accessing
|
||||||
|
tweetsByTimeSpan: divisions
|
||||||
|
|
||||||
|
| tweetsByTimeSpan xAxis labels |
|
||||||
|
tweetsByTimeSpan := self collectMessages: [ self tweets] byTimeSpanSplits: divisions.
|
||||||
|
xAxis := OrderedCollection new.
|
||||||
|
(tweetsByTimeSpan values collect: [ :collection | collection size ]) do: [ :number |
|
||||||
|
xAxis add: ($' asString), (number asString), ($' asString)
|
||||||
|
].
|
||||||
|
labels := OrderedCollection new.
|
||||||
|
tweetsByTimeSpan keys do: [ :string |
|
||||||
|
labels add: ($' asString), string, ($' asString)
|
||||||
|
].
|
||||||
|
^ {
|
||||||
|
tweetsByTimeSpan.
|
||||||
|
('[', (',' join: xAxis), ']').
|
||||||
|
('[', (',' join: labels), ']').
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
|||||||
|
accessing
|
||||||
|
tweetsByWeeksTimeSpan
|
||||||
|
|
||||||
|
| tweetsByTimeSpan xAxis labels |
|
||||||
|
tweetsByTimeSpan := self collectMessages: [ self tweets] byTimeSpanSplits: self tweetsDivisionsByWeeks.
|
||||||
|
xAxis := OrderedCollection new.
|
||||||
|
(tweetsByTimeSpan values collect: [ :collection | collection size ]) do: [ :number |
|
||||||
|
xAxis add: ($' asString), (number asString), ($' asString)
|
||||||
|
].
|
||||||
|
labels := OrderedCollection new.
|
||||||
|
tweetsByTimeSpan keys do: [ :string |
|
||||||
|
labels add: ($' asString), string, ($' asString)
|
||||||
|
].
|
||||||
|
^ {
|
||||||
|
tweetsByTimeSpan.
|
||||||
|
('[', (',' join: xAxis), ']').
|
||||||
|
('[', (',' join: labels), ']').
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
|||||||
|
accessing
|
||||||
|
tweetsDivisionsByWeeks
|
||||||
|
|
||||||
|
| weeks floor divisions |
|
||||||
|
weeks := ((self newestTweet created - self oldestTweet created) days / 7).
|
||||||
|
floor := weeks floor.
|
||||||
|
(weeks - floor) > 0.4
|
||||||
|
ifTrue: [ floor = 0
|
||||||
|
ifTrue: [ divisions := 1 ]
|
||||||
|
ifFalse: [ divisions := floor]]
|
||||||
|
ifFalse: [ divisions := floor + 1 ].
|
||||||
|
^ divisions
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user