From 5b7e37c3854ffb0dcf5bd11c6600d49c99f17c00 Mon Sep 17 00:00:00 2001
From: Hugo Trentesaux <hugo@trentesaux.fr>
Date: Fri, 17 Jul 2020 00:32:58 +0200
Subject: [PATCH] [feat] iterator over blockchain

---
 duniterpy/localblockchain.py | 61 ++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)
 create mode 100644 duniterpy/localblockchain.py

diff --git a/duniterpy/localblockchain.py b/duniterpy/localblockchain.py
new file mode 100644
index 00000000..e8b3c779
--- /dev/null
+++ b/duniterpy/localblockchain.py
@@ -0,0 +1,61 @@
+# imports locally stored blockchain in the chunk format
+# example usage :
+# ```
+# from duniterpy.localblockchain import load
+# bc = load() # gets blockchain iterator
+# b = next(bc) # gets block
+# ```
+
+
+import json
+import os
+import glob
+from .documents import Block
+
+CHUNK_SIZE = 250
+
+class JsonBlockchain():
+    def __init__(self, folder):
+        self.folder = folder # folder where chunks are stored
+        self.chunks = len(glob.glob(os.path.join(folder, "chunk_*"))) # number of files starting with "chunk_"
+        self.current_block_in_chunk = 0 # number from 0 to 249 equal to current_block // current_chunk
+        self.current_chunk = 0 # current chunk number
+        self.chunk = [] # parsed json for current chunk (length = 250)
+        self.parsechunk() # parse first chunk
+
+    def parsechunk(self):
+        """parse a json chunk file"""
+        with open(os.path.join(self.folder, f"chunk_{self.current_chunk}-250.json")) as f:
+            s = f.read()
+            p = json.loads(s)
+            self.chunk = p["blocks"]
+
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        """if current block is outside current chunk, parse next one, otherwise, return current block parsed json"""
+        if self.current_block_in_chunk == 250: # block outside chunk
+            self.current_block_in_chunk = 0 # reset to next chunk start
+            self.current_chunk += 1 # increment current chunk number
+            if self.current_chunk >= self.chunks: # outside range
+                raise StopIteration()
+            self.parsechunk() # parse this chunk
+        self.current_block_in_chunk += 1 # increment current block number for next iteration
+        return self.chunk[self.current_block_in_chunk-1] # return block (before incrementation)
+
+def Blockchain(json_blockchain):
+    """convert json to duniterpy block document"""
+    jbc = json_blockchain
+    for jb in jbc:
+        yield Block.from_parsed_json(jb)
+
+def load(path=".config/duniter/duniter_default/g1/"):
+    """returns an iterator allowing to browse all the blockchain
+    in practice, it will load chunk by chunk and only keep one in memory at a time"""
+    path = os.path.join(os.path.expanduser("~"), path) # expand path
+    jbc = JsonBlockchain(path) # gets an iterator over json blockchain
+    bc = Blockchain(jbc) # convert it to an iterator over blocks
+    return bc # returns
+
+
-- 
GitLab