scribelite 0.2.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0608ffa940a6f65b4432a6e8e494ec2213e20a6fb661e15eafb9f0408f1175be'
4
- data.tar.gz: 5b019fbe73890c1b20a5c4106a617e2c573431e805b47825fb056f7bd50449aa
3
+ metadata.gz: d2a98f4c256e13196f739fb4f538a954f50f51b85ed4bd3fedb3041e66dd3580
4
+ data.tar.gz: 5e9009cef8b739e5e02d630f0e751528a93665d81ba08cecb6ac0e50f8739875
5
5
  SHA512:
6
- metadata.gz: 3a363643f683a32c1ccbd04448d790b6582f5ccc7d8d3176ab65506ff92d826a5b54e37b41add46bb8c8b577a8f76aa9d8ee7800a7d946aa7cf5ce9802fb283b
7
- data.tar.gz: 491db0258ff4a4dc27974f9e3e8e8147a9437690dfdd2c30bf8d81b8710aee0f7ee4a119cd3657b6d48f1cc3399570e4787ef9fbdc87d17ec4f4115acb79d1e7
6
+ metadata.gz: 2b920e7a86fe0c47a4b75870c2ae1d27805210b82511bab685cca55af763a93f4f7c9b3124d449eb350b2f22953a920119749b389f6cb357ce5b5750f5282633
7
+ data.tar.gz: 69177a203454d697914a49bf9cfc1b7bc94e00cb149b4a012d01e808433fe7ab0c6d6b3748de8222860d9860a265d2e7d82dc7dcb7c30f246001646aa0a39ec0
data/CHANGELOG.md CHANGED
@@ -1,4 +1,5 @@
1
- ### 0.2.0
1
+ ### 1.0.0
2
+ ### 0.2.1
2
3
  ### 0.0.1 / 2023-11-21
3
4
 
4
5
  * Everything is new. First release
data/Manifest.txt CHANGED
@@ -3,6 +3,7 @@ Manifest.txt
3
3
  README.md
4
4
  Rakefile
5
5
  lib/scribelite.rb
6
+ lib/scribelite/importer.rb
6
7
  lib/scribelite/models/forward.rb
7
8
  lib/scribelite/models/scribe.rb
8
9
  lib/scribelite/models/tx.rb
data/README.md CHANGED
@@ -4,8 +4,8 @@ scribelite - inscription / inscribe (ethscription calldata) database for ethereu
4
4
 
5
5
 
6
6
 
7
- * home :: [github.com/s6ruby/rubidity](https://github.com/s6ruby/rubidity)
8
- * bugs :: [github.com/s6ruby/rubidity/issues](https://github.com/s6ruby/rubidity/issues)
7
+ * home :: [github.com/0xCompute/ethscribe](https://github.com/0xCompute/ethscribe)
8
+ * bugs :: [github.com/0xCompute/ethscribe/issues](https://github.com/0xCompute/ethscribe/issues)
9
9
  * gem :: [rubygems.org/gems/scribelite](https://rubygems.org/gems/scribelite)
10
10
  * rdoc :: [rubydoc.info/gems/scribelite](http://rubydoc.info/gems/scribelite)
11
11
 
@@ -25,24 +25,23 @@ The work-in-progess database schema looks like:
25
25
  ActiveRecord::Schema.define do
26
26
 
27
27
  create_table :scribes, :id => :string do |t|
28
- t.integer :num, null: false, index: { unique: true, name: 'scribe_nums' }
29
- t.integer :bytes
30
- t.string :content_type
28
+ t.integer :num, null: false, index: { unique: true, name: 'scribe_nums' }
29
+ t.integer :bytes
30
+ t.string :content_type
31
31
  ## add allow duplicate opt-in protocol flag e.g. esip6
32
32
  t.boolean :duplicate ## allows duplicates flag
33
33
  t.boolean :flagged, null: false, default: false ## censored flag / removed on request
34
- ## move sha to tx - why? why not?
35
34
  t.string :sha, null: false ## sha hash as hexstring (but no leading 0)
36
35
  end
37
36
 
38
37
  create_table :txs, :id => :string do |t|
39
38
  t.binary :data # , null: false
40
- t.datetime :date, null: false
39
+ t.datetime :date, null: false
41
40
  t.integer :block, null: false
42
41
  t.integer :idx, null: false ## transaction index (number)
43
42
 
44
- t.string :from, null: false
45
- t.string :to, null: false
43
+ t.string :from, null: false
44
+ t.string :to, null: false
46
45
 
47
46
  t.integer :fee
48
47
  t.integer :value
@@ -62,6 +61,20 @@ ScribeDb.connect( adapter: 'sqlite3',
62
61
  ScribeDb.create_all ## build schema
63
62
  ```
64
63
 
64
+ and lets import the first hundred (page size is 25) ethscriptions on mainnet (via the ethscriptions.com api):
65
+
66
+
67
+ ``` ruby
68
+ require 'scribelite'
69
+
70
+ ScribeDb.open( './scribe.db' )
71
+
72
+ (1..4).each do |page|
73
+ ScribeDb.import_ethscriptions( page: page )
74
+ end
75
+ ```
76
+
77
+
65
78
 
66
79
  and to query use it like:
67
80
 
@@ -93,9 +106,28 @@ Scribe.order( :num ).limit(limit).each do |scribe|
93
106
  print "#{scribe.num} / #{scribe.content_type} - #{scribe.tx.date} @ #{scribe.tx.block}"
94
107
  print "\n"
95
108
  end
96
- ```
97
109
 
98
110
 
111
+ ## Let's query for all inscriptions grouped by date (day)
112
+ ## and dump the results:
113
+ pp Scribe.counts_by_day
114
+ pp Scribe.counts_by_year
115
+ pp Scribe.counts_by_month
116
+ pp Scribe.counts_by_hour
117
+
118
+
119
+ ## Let's query for all content types and group by count (descending)
120
+ ## and dump the results:
121
+ pp Scribe.counts_by_content_type
122
+
123
+ pp Scribe.counts_by_block
124
+ pp Scribe.counts_by_block_with_timestamp
125
+
126
+ pp Scribe.counts_by_address # from (creator/minter) address
127
+
128
+ # ...
129
+ ```
130
+
99
131
  To be continued...
100
132
 
101
133
 
@@ -110,7 +142,9 @@ at the ruby code commons (rubycocos) org.
110
142
 
111
143
  ## Questions? Comments?
112
144
 
113
- Join us in the [Rubidity (community) discord (chat server)](https://discord.gg/3JRnDUap6y). Yes you can.
145
+ Join us in the [0xCompute discord (chat server)](https://discord.gg/3JRnDUap6y)
146
+ (or in the more general Ethscription discord).
147
+ Yes you can.
114
148
  Your questions and commentary welcome.
115
149
 
116
150
  Or post them over at the [Help & Support](https://github.com/geraldb/help) page. Thanks.
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ Hoe.spec 'scribelite' do
8
8
  self.summary = "scribelite gem - inscription / inscribe (ethscription calldata) database for ethereum & co; let's you query via sql and more"
9
9
  self.description = summary
10
10
 
11
- self.urls = { home: 'https://github.com/s6ruby/rubidity' }
11
+ self.urls = { home: 'https://github.com/0xCompute/ethscribe' }
12
12
 
13
13
  self.author = 'Gerald Bauer'
14
14
  self.email = 'gerald.bauer@gmail.com'
@@ -0,0 +1,155 @@
1
+
2
+
3
+ module ScribeDb
4
+
5
+
6
+
7
+ ## note: by default - sort asc(ending) - oldest first (0,1,2,3, .etc.)
8
+ def self.import_ethscriptions( page: 1,
9
+ per_page: 50,
10
+ sort_order: 'asc' )
11
+ net = Ethscribe.config.client
12
+ recs = net.ethscriptions( page: page,
13
+ per_page: per_page,
14
+ sort_order: sort_order )
15
+
16
+ puts " #{recs.size} record(s)"
17
+
18
+ recs.each_with_index do |rec,i|
19
+ puts "==> page #{page}/#{i+1} - #{rec['transaction_hash']}..."
20
+ _import_ethscription( rec )
21
+ end
22
+
23
+ recs.size ## return number of records fetched for now
24
+ end # method import_ethscriptions
25
+
26
+
27
+ def self.sync_facet_txs
28
+ net = Ethscribe.config.client
29
+
30
+ ## add block argument - why? why not?
31
+ ## get count by block e.g.
32
+ ## our_count = Scribe.joins(:tx).where( 'block < ?', new_block_number).count
33
+
34
+
35
+ count = Scribe.count
36
+
37
+ block = if count > 0
38
+ last_scribe = Scribe.where( num: Scribe.maximum(:num) ).first
39
+ last_scribe.tx.block + 1
40
+ else
41
+ 0
42
+ end
43
+
44
+ loop do
45
+ res = net.newer_facet_txs( block, max: 2500, count: count )
46
+
47
+ break if res['blocks'].size == 0
48
+
49
+ first_block = res['blocks'][0]['block_number']
50
+ last_block = res['blocks'][-1]['block_number']
51
+ puts "==> #{res['blocks'].size} block(s) - #{first_block} to #{last_block}..."
52
+ puts " total_future_ethscriptions: #{res['total_future_ethscriptions']}"
53
+
54
+ batch_count = 0
55
+ res['blocks'].each do |block_rec|
56
+ block_rec['ethscriptions'].each do |rec|
57
+ _import_ethscription( rec )
58
+ count += 1
59
+ batch_count += 1
60
+ end
61
+ end
62
+ puts " #{batch_count} record(s) added"
63
+
64
+ block = last_block + 1
65
+
66
+ break if res['total_future_ethscriptions'] == 0
67
+ end
68
+ end # method sync_facet_txs
69
+ class << self
70
+ alias_method :sync_facet_txns, :sync_facet_txs
71
+ end
72
+
73
+
74
+
75
+ def self._import_ethscription( rec )
76
+ txid = rec['transaction_hash']
77
+ block = rec['block_number']
78
+ idx = rec['transaction_index']
79
+
80
+ from = rec['creator']
81
+ to = rec['initial_owner']
82
+
83
+ ## todo - double check if daylight saving time (dst) breaks timestamp == utc identity/conversion?
84
+ ## 2001-02-03T04:05:06+07:00
85
+ ## 2016-05-29T22:28:15.000Z
86
+ ## check if %z can handle .000Z ??
87
+ ## DateTime.strptime( '2016-05-29T22:28:15.000Z', '%Y-%m-%dT%H:%M:%S%z' )
88
+ ## pp rec['creation_timestamp']
89
+ date = DateTime.strptime( rec['creation_timestamp'], '%Y-%m-%dT%H:%M:%S.000Z' )
90
+
91
+
92
+ num = rec['ethscription_number']
93
+ content_type = rec['mimetype']
94
+ data = rec['content_uri']
95
+ sha = rec['sha']
96
+
97
+ duplicate = rec['esip6']
98
+ flagged = rec['image_removed_by_request_of_rights_holder']
99
+
100
+ ### check - if flagged
101
+ ## content_type always set to text/plain
102
+ ## and data to data:, ???
103
+ ## (re)set to nil/null - why? why not?
104
+ if flagged
105
+ data = nil
106
+ content_type = nil
107
+ end
108
+
109
+ tx_attribs = {
110
+ id: txid,
111
+ block: block,
112
+ idx: idx,
113
+ date: date,
114
+ from: from,
115
+ to: to,
116
+ data: data, ## note: for now saved as utf8 string (not hex!!!!)
117
+ }
118
+
119
+ scribe_attribs = {
120
+ id: txid,
121
+ num: num,
122
+ content_type: content_type,
123
+ duplicate: duplicate,
124
+ flagged: flagged,
125
+ sha: sha,
126
+ bytes: data ? data.length : nil ## auto-add/calc bytes (content length)
127
+ }
128
+
129
+ if num.nil?
130
+ puts "!! skipping unconfirmed / unassigned inscribe - no. num"
131
+ return
132
+ end
133
+
134
+
135
+ scribe = Scribe.find_by( id: txid )
136
+ if scribe
137
+ ## skip for now - found id db
138
+ else
139
+ # puts "scribe_attribs:"
140
+ # pp scribe_attribs
141
+ Scribe.create!( **scribe_attribs )
142
+ end
143
+
144
+ tx = Tx.find_by( id: txid )
145
+ if tx
146
+ ## skip for now - found id db
147
+ else
148
+ # puts "tx_attribs:"
149
+ # pp tx_attribs
150
+ Tx.create!( **tx_attribs )
151
+ end
152
+ end
153
+
154
+ end # module ScribeDb
155
+
@@ -17,11 +17,18 @@ module ScribeDb
17
17
  ### scope like helpers
18
18
  def self.png() where( content_type: 'image/png' ); end
19
19
  def self.gif() where( content_type: 'image/gif' ); end
20
- def self.jpg() where( content_type: 'image/jpeg' ); end
20
+ def self.jpg() where( content_type: ['image/jpeg',
21
+ 'image/jpg'] ); end
21
22
  def self.webp() where( content_type: 'image/webp' ); end
22
23
  def self.svg() where( content_type: 'image/svg+xml' ); end
23
24
  def self.avif() where( content_type: 'image/avif' ); end
24
25
 
26
+ def self.pdf() where( content_type: 'application/pdf' ); end
27
+
28
+ def self.facet() where( content_type: 'application/vnd.facet.tx+json' ); end
29
+
30
+
31
+
25
32
  class << self
26
33
  alias_method :jpeg, :jpg
27
34
  end
@@ -31,6 +38,7 @@ module ScribeDb
31
38
  where( content_type: [
32
39
  'image/png',
33
40
  'image/jpeg',
41
+ 'image/jpg',
34
42
  'image/gif',
35
43
  'image/webp',
36
44
  'image/svg+xml',
@@ -98,90 +106,63 @@ module ScribeDb
98
106
  .order( Arel.sql( 'COUNT(*) DESC, content_type')).count
99
107
  end
100
108
 
101
- class << self
102
- alias_method :biggest, :largest
103
- alias_method :counts_by_content_type, :content_type_counts
109
+
110
+ def self.block_counts
111
+ joins(:tx).group( 'block' )
112
+ .order( 'block').count
113
+ end
114
+
115
+ def self.block_with_timestamp_counts
116
+ joins(:tx).group( Arel.sql( "block || ' @ ' || date" ))
117
+ .order( 'block' ).count
104
118
  end
105
119
 
106
- ###
107
- # instance methods
108
- def extname
109
- ## map mime type to file extname
110
- ## see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
111
- ## for real-world usage, see https://dune.com/dgtl_assets/bitcoin-ordinals-analysis
112
- ## https://github.com/casey/ord/blob/master/src/media.rs
113
-
114
- if content_type.start_with?( 'text/plain' )
115
- '.txt'
116
- elsif content_type.start_with?( 'text/markdown' )
117
- '.md'
118
- elsif content_type.start_with?( 'text/html' )
119
- '.html'
120
- elsif content_type.start_with?( 'text/javascript' ) ||
121
- content_type.start_with?( 'application/javascript' )
122
- ## note: application/javascript is considered bad practice/legacy
123
- '.js'
124
- elsif content_type.start_with?( 'image/png' )
125
- ## Portable Network Graphics (PNG)
126
- '.png'
127
- elsif content_type.start_with?( 'image/jpeg' )
128
- ## Joint Photographic Expert Group image (JPEG)
129
- '.jpg' ## use jpeg - why? why not?
130
- elsif content_type.start_with?( 'image/webp' )
131
- ## Web Picture format (WEBP)
132
- '.webp' ## note: no three-letter extension available
133
- elsif content_type.start_with?( 'image/svg' )
134
- ## Scalable Vector Graphics (SVG)
135
- '.svg'
136
- elsif content_type.start_with?( 'image/gif' )
137
- ## Graphics Interchange Format (GIF)
138
- '.gif'
139
- elsif content_type.start_with?( 'image/avif' )
140
- ## AV1 Image File Format (AVIF)
141
- '.avif'
142
- elsif content_type.start_with?( 'application/epub' )
143
- '.epub'
144
- elsif content_type.start_with?( 'application/pdf' )
145
- '.pdf'
146
- elsif content_type.start_with?( 'application/json' )
147
- '.json'
148
- elsif content_type.start_with?( 'application/pgp-signature' )
149
- '.sig'
150
- elsif content_type.start_with?( 'audio/mpeg' )
151
- '.mp3'
152
- elsif content_type.start_with?( 'audio/midi' )
153
- '.midi'
154
- elsif content_type.start_with?( 'video/mp4' )
155
- '.mp4'
156
- elsif content_type.start_with?( 'video/webm' )
157
- '.wepm'
158
- elsif content_type.start_with?( 'audio/mod' )
159
- ## is typo? possible? only one inscription in 20m?
160
- '.mod' ## check/todo/fix if is .wav??
161
- else
162
- puts "!! ERROR - no file extension configured for content type >#{content_type}<; sorry:"
163
- pp self
164
- exit 1
120
+
121
+ def self.date_counts
122
+ ## note: strftime is SQLite specific/only!!!
123
+ joins(:tx).group( Arel.sql("strftime('%Y-%m-%d', date)"))
124
+ .order( Arel.sql("strftime('%Y-%m-%d', date)")).count
165
125
  end
166
- end
167
-
168
- =begin
169
- def export_path ## default export path
170
- numstr = "%08d" % num ### e.g. 00000001
171
- "./tmp/#{numstr}#{extname}"
172
- end
173
- def export( path=export_path )
174
- if blob
175
- write_blob( path, blob.content )
176
- else
177
- ## todo/fix: raise exception - no content
178
- puts "!! ERROR - inscribe has no content (blob); sorry:"
179
- pp self
180
- exit 1
126
+
127
+ def self.month_counts
128
+ ## note: strftime is SQLite specific/only!!!
129
+ joins(:tx).group( Arel.sql("strftime('%Y-%m', date)"))
130
+ .order( Arel.sql("strftime('%Y-%m', date)")).count
181
131
  end
182
- end
183
- =end
184
132
 
133
+ def self.year_counts
134
+ ## note: strftime is SQLite specific/only!!!
135
+ joins(:tx).group( Arel.sql("strftime('%Y', date)"))
136
+ .order( Arel.sql("strftime('%Y', date)")).count
137
+ end
138
+
139
+ def self.hour_counts
140
+ ## note: strftime is SQLite specific/only!!!
141
+ joins(:tx).group( Arel.sql("strftime('%Y-%m-%d %Hh', date)"))
142
+ .order( Arel.sql("strftime('%Y-%m-%d %Hh', date)")).count
143
+ end
144
+
145
+
146
+ def self.from_counts
147
+ ## note: from is sql keyword!!!
148
+ ## wrap in [] for sqlite - check if works for others!!!
149
+ joins(:tx).group( '[from]' )
150
+ .order( Arel.sql( 'COUNT(*) DESC')).count
151
+ end
152
+
153
+
154
+ class << self
155
+ alias_method :biggest, :largest
156
+ alias_method :counts_by_content_type, :content_type_counts
157
+ alias_method :counts_by_date, :date_counts
158
+ alias_method :counts_by_day, :date_counts
159
+ alias_method :counts_by_month, :month_counts
160
+ alias_method :counts_by_year, :year_counts
161
+ alias_method :counts_by_hour, :hour_counts
162
+ alias_method :counts_by_block, :block_counts
163
+ alias_method :counts_by_block_with_timestamp, :block_with_timestamp_counts
164
+ alias_method :counts_by_address, :from_counts
165
+ end
185
166
  end # class Scribe
186
167
 
187
168
  end # module Model
@@ -1,7 +1,7 @@
1
1
 
2
2
  module Scribelite
3
- MAJOR = 0 ## todo: namespace inside version or something - why? why not??
4
- MINOR = 2
3
+ MAJOR = 1 ## todo: namespace inside version or something - why? why not??
4
+ MINOR = 0
5
5
  PATCH = 0
6
6
  VERSION = [MAJOR,MINOR,PATCH].join('.')
7
7
 
data/lib/scribelite.rb CHANGED
@@ -34,8 +34,6 @@ require_relative 'scribelite/models/tx'
34
34
 
35
35
  require_relative 'scribelite/schema'
36
36
 
37
- # require_relative 'cache'
38
- # require_relative 'importer' ## note: require (soft dep) ordinals gems!!!
39
37
 
40
38
 
41
39
 
@@ -134,6 +132,11 @@ end # module ScribeDb
134
132
 
135
133
 
136
134
 
135
+ # add ethscriptions / ethscripe importer - why? why not?
136
+ require_relative 'scribelite/importer'
137
+
138
+
139
+
137
140
 
138
141
 
139
142
  ## add convenience helpers
@@ -141,6 +144,13 @@ Scribe = ScribeDb::Model::Scribe
141
144
  Tx = ScribeDb::Model::Tx
142
145
 
143
146
 
147
+
148
+ require 'active_support/number_helper'
149
+ include ActiveSupport::NumberHelper ## e.g. number_to_human_size
150
+
151
+
152
+
153
+
144
154
  # say hello
145
155
  puts Scribelite.banner ## if defined?($RUBYCOCOS_DEBUG) && $RUBCOCOS_DEBUG
146
156
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scribelite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-25 00:00:00.000000000 Z
11
+ date: 2024-04-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ethscribe
@@ -162,14 +162,14 @@ dependencies:
162
162
  requirements:
163
163
  - - "~>"
164
164
  - !ruby/object:Gem::Version
165
- version: '4.0'
165
+ version: '4.1'
166
166
  type: :development
167
167
  prerelease: false
168
168
  version_requirements: !ruby/object:Gem::Requirement
169
169
  requirements:
170
170
  - - "~>"
171
171
  - !ruby/object:Gem::Version
172
- version: '4.0'
172
+ version: '4.1'
173
173
  description: scribelite gem - inscription / inscribe (ethscription calldata) database
174
174
  for ethereum & co; let's you query via sql and more
175
175
  email: gerald.bauer@gmail.com
@@ -185,12 +185,13 @@ files:
185
185
  - README.md
186
186
  - Rakefile
187
187
  - lib/scribelite.rb
188
+ - lib/scribelite/importer.rb
188
189
  - lib/scribelite/models/forward.rb
189
190
  - lib/scribelite/models/scribe.rb
190
191
  - lib/scribelite/models/tx.rb
191
192
  - lib/scribelite/schema.rb
192
193
  - lib/scribelite/version.rb
193
- homepage: https://github.com/s6ruby/rubidity
194
+ homepage: https://github.com/0xCompute/ethscribe
194
195
  licenses:
195
196
  - Public Domain
196
197
  metadata: {}