1597071600
When I pressed F5 to start debugging and Visual Studio found a compile-time error, nothing irritated me more than the dialog box Visual Studio popped up that asked, “There were build errors. Would you like to continue and run the last successful build?”
Let me be clear: No, I didn’t want to run “the last successful build.” I never wanted to run “the last successful build.” Who in the world would want to run “the last successful build?” Like any other rational human being in the world, I wanted to run the version of the code with the changes I had just finished making … well, after I fixed the compile errors, I mean.
So I turned that idiot message off.
If you also want to get rid of that message, then go to Tools | Options | Projects and Solutions | Build and Run. In the right-hand panel under “On Run, when build or deployment errors occur,” change the selected item in the dropdown list to Do Not Launch. Now, when you have build errors, Visual Studio will just sit there. You’ll have to get into the habit of checking your Error List to find out why you’re not in debug mode but, for me, that didn’t take me very long.
#visual studio code #visual studio #code #visualstudiomagazine
1597071600
When I pressed F5 to start debugging and Visual Studio found a compile-time error, nothing irritated me more than the dialog box Visual Studio popped up that asked, “There were build errors. Would you like to continue and run the last successful build?”
Let me be clear: No, I didn’t want to run “the last successful build.” I never wanted to run “the last successful build.” Who in the world would want to run “the last successful build?” Like any other rational human being in the world, I wanted to run the version of the code with the changes I had just finished making … well, after I fixed the compile errors, I mean.
So I turned that idiot message off.
If you also want to get rid of that message, then go to Tools | Options | Projects and Solutions | Build and Run. In the right-hand panel under “On Run, when build or deployment errors occur,” change the selected item in the dropdown list to Do Not Launch. Now, when you have build errors, Visual Studio will just sit there. You’ll have to get into the habit of checking your Error List to find out why you’re not in debug mode but, for me, that didn’t take me very long.
#visual studio code #visual studio #code #visualstudiomagazine
1624653660
A useful tool several businesses implement for answering questions that potential customers may have is a chatbot. Many programming languages give web designers several ways on how to make a chatbot for their websites. They are capable of answering basic questions for visitors and offer innovation for businesses.
With the help of programming languages, it is possible to create a chatbot from the ground up to satisfy someone’s needs.
Before building a chatbot, it is ideal for web designers to determine how it will function on a website. Several chatbot duties center around fulfilling customers’ needs and questions or compiling and optimizing data via transactions.
Some benefits of implementing chatbots include:
Some programmers may choose to design a chatbox to function through predefined answers based on the questions customers may input or function by adapting and learning via human input.
#chatbots #latest news #the best way to build a chatbot in 2021 #build #build a chatbot #best way to build a chatbot
1571046022
Cryptocurrency is a decentralized digital currency that uses encryption techniques to regulate the generation of currency units and to verify the transfer of funds. Anonymity, decentralization, and security are among its main features. Cryptocurrency is not regulated or tracked by any centralized authority, government, or bank.
Blockchain, a decentralized peer-to-peer (P2P) network, which is comprised of data blocks, is an integral part of cryptocurrency. These blocks chronologically store information about transactions and adhere to a protocol for inter-node communication and validating new blocks. The data recorded in blocks cannot be altered without the alteration of all subsequent blocks.
In this article, we are going to explain how you can create a simple blockchain using the Python programming language.
Here is the basic blueprint of the Python class we’ll use for creating the blockchain:
class Block(object):
def __init__():
pass
#initial structure of the block class
def compute_hash():
pass
#producing the cryptographic hash of each block
class BlockChain(object):
def __init__(self):
#building the chain
def build_genesis(self):
pass
#creating the initial block
def build_block(self, proof_number, previous_hash):
pass
#builds new block and adds to the chain
@staticmethod
def confirm_validity(block, previous_block):
pass
#checks whether the blockchain is valid
def get_data(self, sender, receiver, amount):
pass
# declares data of transactions
@staticmethod
def proof_of_work(last_proof):
pass
#adds to the security of the blockchain
@property
def latest_block(self):
pass
#returns the last block in the chain
Now, let’s explain how the blockchain class works.
Here is the code for our initial block class:
import hashlib
import time
class Block(object):
def __init__(self, index, proof_number, previous_hash, data, timestamp=None):
self.index = index
self.proof_number = proof_number
self.previous_hash = previous_hash
self.data = data
self.timestamp = timestamp or time.time()
@property
def compute_hash(self):
string_block = "{}{}{}{}{}".format(self.index, self.proof_number, self.previous_hash, self.data, self.timestamp)
return hashlib.sha256(string_block.encode()).hexdigest()
As you can see above, the class constructor or initiation method ( init()) above takes the following parameters:
self
— just like any other Python class, this parameter is used to refer to the class itself. Any variable associated with the class can be accessed using it.
index
— it’s used to track the position of a block within the blockchain.
previous_hash
— it used to reference the hash of the previous block within the blockchain.
data—it
gives details of the transactions done, for example, the amount bought.
timestamp—it
inserts a timestamp for all the transactions performed.
The second method in the class, compute_hash , is used to produce the cryptographic hash of each block based on the above values.
As you can see, we imported the SHA-256 algorithm into the cryptocurrency blockchain project to help in getting the hashes of the blocks.
Once the values have been placed inside the hashing module, the algorithm will return a 256-bit string denoting the contents of the block.
So, this is what gives the blockchain immutability. Since each block will be represented by a hash, which will be computed from the hash of the previous block, corrupting any block in the chain will make the other blocks have invalid hashes, resulting in breakage of the whole blockchain network.
The whole concept of a blockchain is based on the fact that the blocks are “chained” to each other. Now, we’ll create a blockchain class that will play the critical role of managing the entire chain.
It will keep the transactions data and include other helper methods for completing various roles, such as adding new blocks.
Let’s talk about the helper methods.
Here is the code:
class BlockChain(object):
def __init__(self):
self.chain = []
self.current_data = []
self.nodes = set()
self.build_genesis()
The init() constructor method is what instantiates the blockchain.
Here are the roles of its attributes:
self.chain — this variable stores all the blocks.
self.current_data — this variable stores information about the transactions in the block.
self.build_genesis() — this method is used to create the initial block in the chain.
The build_genesis()
method is used for creating the initial block in the chain, that is, a block without any predecessors. The genesis block is what represents the beginning of the blockchain.
To create it, we’ll call the build_block()
method and give it some default values. The parameters proof_number
and previous_hash
are both given a value of zero, though you can give them any value you desire.
Here is the code:
def build_genesis(self):
self.build_block(proof_number=0, previous_hash=0)
def build_block(self, proof_number, previous_hash):
block = Block(
index=len(self.chain),
proof_number=proof_number,
previous_hash=previous_hash,
data=self.current_data
)
self.current_data = []
self.chain.append(block)
return block
The confirm_validity
method is critical in examining the integrity of the blockchain and making sure inconsistencies are lacking.
As explained earlier, hashes are pivotal for realizing the security of the cryptocurrency blockchain, because any slight alteration in an object will result in the creation of an entirely different hash.
Thus, the confirm_validity
method utilizes a series of if statements to assess whether the hash of each block has been compromised.
Furthermore, it also compares the hash values of every two successive blocks to identify any anomalies. If the chain is working properly, it returns true; otherwise, it returns false.
Here is the code:
def confirm_validity(block, previous_block):
if previous_block.index + 1 != block.index:
return False
elif previous_block.compute_hash != block.previous_hash:
return False
elif block.timestamp <= previous_block.timestamp:
return False
return True
The get_data
method is important in declaring the data of transactions on a block. This method takes three parameters (sender’s information, receiver’s information, and amount) and adds the transaction data to the self.current_data list.
Here is the code:
def get_data(self, sender, receiver, amount):
self.current_data.append({
'sender': sender,
'receiver': receiver,
'amount': amount
})
return True
In blockchain technology, Proof of Work (PoW) refers to the complexity involved in mining or generating new blocks on the blockchain.
For example, the PoW can be implemented by identifying a number that solves a problem whenever a user completes some computing work. Anyone on the blockchain network should find the number complex to identify but easy to verify — this is the main concept of PoW.
This way, it discourages spamming and compromising the integrity of the network.
In this article, we’ll illustrate how to include a Proof of Work algorithm in a blockchain cryptocurrency project.
Finally, the latest_block() helper method is used for retrieving the last block on the network, which is actually the current block.
Here is the code:
def latest_block(self):
return self.chain[-1]
Now, this is the most exciting section!
Initially, the transactions are kept in a list of unverified transactions. Mining refers to the process of placing the unverified transactions in a block and solving the PoW problem. It can be referred to as the computing work involved in verifying the transactions.
If everything has been figured out correctly, a block is created or mined and joined together with the others in the blockchain. If users have successfully mined a block, they are often rewarded for using their computing resources to solve the PoW problem.
Here is the mining method in this simple cryptocurrency blockchain project:
def block_mining(self, details_miner):
self.get_data(
sender="0", #it implies that this node has created a new block
receiver=details_miner,
quantity=1, #creating a new block (or identifying the proof number) is awarded with 1
)
last_block = self.latest_block
last_proof_number = last_block.proof_number
proof_number = self.proof_of_work(last_proof_number)
last_hash = last_block.compute_hash
block = self.build_block(proof_number, last_hash)
return vars(block)
Here is the whole code for our crypto blockchain class in Python:
import hashlib
import time
class Block(object):
def __init__(self, index, proof_number, previous_hash, data, timestamp=None):
self.index = index
self.proof_number = proof_number
self.previous_hash = previous_hash
self.data = data
self.timestamp = timestamp or time.time()
@property
def compute_hash(self):
string_block = "{}{}{}{}{}".format(self.index, self.proof_number, self.previous_hash, self.data, self.timestamp)
return hashlib.sha256(string_block.encode()).hexdigest()
def __repr__(self):
return "{} - {} - {} - {} - {}".format(self.index, self.proof_number, self.previous_hash, self.data, self.timestamp)
class BlockChain(object):
def __init__(self):
self.chain = []
self.current_data = []
self.nodes = set()
self.build_genesis()
def build_genesis(self):
self.build_block(proof_number=0, previous_hash=0)
def build_block(self, proof_number, previous_hash):
block = Block(
index=len(self.chain),
proof_number=proof_number,
previous_hash=previous_hash,
data=self.current_data
)
self.current_data = []
self.chain.append(block)
return block
@staticmethod
def confirm_validity(block, previous_block):
if previous_block.index + 1 != block.index:
return False
elif previous_block.compute_hash != block.previous_hash:
return False
elif block.timestamp <= previous_block.timestamp:
return False
return True
def get_data(self, sender, receiver, amount):
self.current_data.append({
'sender': sender,
'receiver': receiver,
'amount': amount
})
return True
@staticmethod
def proof_of_work(last_proof):
pass
@property
def latest_block(self):
return self.chain[-1]
def chain_validity(self):
pass
def block_mining(self, details_miner):
self.get_data(
sender="0", #it implies that this node has created a new block
receiver=details_miner,
quantity=1, #creating a new block (or identifying the proof number) is awared with 1
)
last_block = self.latest_block
last_proof_number = last_block.proof_number
proof_number = self.proof_of_work(last_proof_number)
last_hash = last_block.compute_hash
block = self.build_block(proof_number, last_hash)
return vars(block)
def create_node(self, address):
self.nodes.add(address)
return True
@staticmethod
def get_block_object(block_data):
return Block(
block_data['index'],
block_data['proof_number'],
block_data['previous_hash'],
block_data['data'],
timestamp=block_data['timestamp']
)
blockchain = BlockChain()
print("GET READY MINING ABOUT TO START")
print(blockchain.chain)
last_block = blockchain.latest_block
last_proof_number = last_block.proof_number
proof_number = blockchain.proof_of_work(last_proof_number)
blockchain.get_data(
sender="0", #this means that this node has constructed another block
receiver="LiveEdu.tv",
amount=1, #building a new block (or figuring out the proof number) is awarded with 1
)
last_hash = last_block.compute_hash
block = blockchain.build_block(proof_number, last_hash)
print("WOW, MINING HAS BEEN SUCCESSFUL!")
print(blockchain.chain)
Now, let’s try to run our code to see if we can generate some digital coins…
Wow, it worked!
That is it!
We hope that this article has assisted you to understand the underlying technology that powers cryptocurrencies such as Bitcoin and Ethereum.
We just illustrated the basic ideas for making your feet wet in the innovative blockchain technology. The project above can still be enhanced by incorporating other features to make it more useful and robust.
Thanks for reading !
Do you have any comments or questions? Please share them below.
#python #cryptocurrency
1633583160
従来のWebアプリケーションは本質的に同期しています。 ユーザーはブラウザーに表示されるWebインターフェースを操作し、ブラウザーはそのユーザー操作に基づいてサーバーに要求を返し、サーバーはユーザーの新しい表示でそれらの要求に応答します。
今日、状況は変化しました。現代のWebサイトは、数十万の訪問者からの要求を処理する必要があります。これらの要求にデータベースまたはWebサービスとの対話が含まれる場合、応答時間が長くなり、何千もの訪問者が同じリソースにアクセスしている場合、Webサイトのパフォーマンスが大幅に低下する可能性があります。 ここで非同期Webが助けになります。
非同期性を選択したときに把握できる利点のいくつかを次に示します。
このチュートリアルでは、新しい要求に応答するWebサーバーの機能を制限する長時間実行タスクを処理するWebアプリケーションを構築するときに発生する一般的な落とし穴の1つを克服する方法を説明します。
簡単な解決策は、これらの長時間実行タスクをバックグラウンドで非同期に、別のスレッドまたはプロセス内で実行し、Webサーバーを解放することです。
Redis、Flask、Celery、SocketIOなどのいくつかのコンポーネントを活用して、長時間実行されるタスクの実行をオフロードし、完了したら、クライアントにそのステータスを示すプッシュ通知を送信します。
このチュートリアルでは、コルーチンを使用してコードを同時に実行できるasyncioPythonの組み込みライブラリについては説明していません。
要件が満たされると、次のコンポーネントが機能します。
Redis:は、オープンソースの高度なKey-Valueストアであり、高性能でスケーラブルなWebアプリケーションを構築するための適切なソリューションです。それを際立たせる3つの主な特徴があります:
Redisはデータベースを完全にメモリに保持し、永続性のためにのみディスクを使用します。
多くのKey-Valueデータストアと比較すると、Redisには比較的豊富なデータ型のセットがあります。
Redisのインストールは、このチュートリアルの範囲外です。ただし、Windowsマシンにインストールするには、このクイックガイドに従うことをお勧めします。
LinuxまたはmacOSを使用している場合は、以下のコマンドのいずれかを実行すると、Redisがセットアップされます。
Ubuntu / Debian:
$ sudo apt-get install redis-server
マックOS:
$ brew install redis
$ brew services start redis
注意: このチュートリアルでは、Redisバージョン3.0.504を使用しています
セロリ:Pythonの世界で最も人気のあるバックグラウンドジョブマネージャーの1人です。リアルタイム操作に重点を置いていますが、スケジューリングもサポートしています。RedisやRabbitMQなどのいくつかのメッセージブローカーと互換性があり、プロデューサーとコンシューマーの両方として機能できます。
requirements.txt
ファイルにCeleryをインストールし ます。
注意: このチュートリアルでは、Celeryバージョン4.4.7を使用しています。
このチュートリアルでは、スキャフォールディング手法を採用し、同期通信と非同期通信の違い、および非同期通信のバリエーションを理解するために、一連のさまざまなシナリオについて説明します。
すべてのシナリオはFlaskフレームワーク内で提示されます。ただし、それらのほとんどは他のPythonフレームワーク(Django、Pyramid)に簡単に移植できます。
このチュートリアルに興味をそそられ、すぐにコードに飛び込みたいと思う 場合は、この記事で使用されているコードについて、このGithubリポジトリにアクセス してください。
私たちのアプリケーションは以下で構成されます:
app_sync.py
同期通信を紹介するプログラム 。app_async1.py
クライアントがポーリングメカニズムを使用してサーバー側プロセスのフィードバックを要求する可能性がある非同期サービス呼び出しを示すプログラム 。app_async2.py
クライアントへの自動フィードバックを伴う非同期サービス呼び出しを示すプログラム 。app_async3.py
クライアントへの自動フィードバックを伴う、スケジュール後の非同期サービス呼び出しを示すプログラム 。セットアップに飛び込みましょう。もちろん 、システムにPython3をインストールする必要があり ます。私は必要なライブラリをインストールする仮想環境を使用します(そしてあなたも間違いなくそうするべきです):
$ python -m venv async-venv
$ source async-venv/bin/activate
名前の付いたファイルを作成し、 requirements.txt
その中に次の行を追加します。
Flask==1.1.2
Flask-SocketIO==5.0.1
Celery==4.4.7
redis==3.5.3
gevent==21.1.2
gevent-websocket==0.10.1
flower==0.9.7
今それらをインストールします:
$ pip install -r requirements.txt
このチュートリアルを終了すると、フォルダー構造は次のようになります。
それがクリアされたので、実際のコードを書き始めましょう。
まず、アプリケーションの構成パラメータを次のように定義します config.py
。
#config.py
#Application configuration File
################################
#Secret key that will be used by Flask for securely signing the session cookie
# and can be used for other security related needs
SECRET_KEY = 'SECRET_KEY'
#Map to REDIS Server Port
BROKER_URL = 'redis://localhost:6379'
#Minimum interval of wait time for our task
MIN_WAIT_TIME = 1
#Maximum interval of wait time for our task
MAX_WAIT_TIME = 20
注意:簡潔にするために、これらの構成パラメーターをにハードコーディングしましたが config.py
、これらのパラメーターを別のファイル(たとえば.env
)に保存することをお勧めします 。
次に、プロジェクトの初期化ファイルを次の場所に作成します init.py
。
#init.py
from flask import Flask
#Create a flask instance
app = Flask(__name__)
#Loads flask configurations from config.py
app.secret_key = app.config['SECRET_KEY']
app.config.from_object("config")
#Setup the Flask SocketIO integration (Required only for asynchronous scenarios)
from flask_socketio import SocketIO
socketio = SocketIO(app,logger=True,engineio_logger=True,message_queue=app.config['BROKER_URL'])
コーディングに入る前に、同期通信について簡単に説明します。
で同期通信、発呼者がサービスを要求し、完全にサービスを待ち受けます。そのサービスの結果を受け取った場合にのみ、作業を続行します。タイムアウトを定義して、定義された期間内にサービスが終了しない場合、呼び出しは失敗したと見なされ、呼び出し元は続行します。
同期通信がどのように機能するかを理解するために、専用のウェイターが割り当てられたと想像してください。彼はあなたの注文を受け取り、それをキッチンに届け、そこでシェフがあなたの料理を準備するのを待ちます。この間、ウェイターは何もしていません。
次の図は、同期サービス呼び出しを示しています。
同期通信はシーケンシャルタスクに適していますが、同時タスクが多数ある場合、プログラムはスレッドを使い果たし、スレッドが使用可能になるまで新しいタスクを待機させる可能性があります。
それでは、コーディングに取り掛かりましょう。Flaskがレンダリングできるテンプレート(index.html
)を作成し、その中に次のHTMLコードを含めます。
templates/index.html
<!DOCTYPE html>
<html>
<head>
<title>Synchronicity versus Asynchronicity</title>
<link rel="stylesheet" href="{{url_for('static',filename='css/materialize.min.css')}}">
<script src="{{ url_for('static',filename='js/jquery.min.js') }}"></script>
<script src="{{ url_for('static',filename='js/socket.io.js') }}"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body class="container">
<div class="row">
<h5>Click to start a post scheduled ansycnhronous task with automatic feedback.</h5>
</div>
<div class="card-panel">
<form method='post' id="runTaskForm" action="/runPSATask">
<div>
<input id="duration" name="duration" placeholder="Enter duration in seconds. for example: 30" type="text">
<label for="duration">Duration</label>
</div>
<button style="height:50px;width:600px" type="submit" id="runTask">Run A Post Scheduled Asynchronous Task With Automatic Feedback</button>
</form>
</div>
<div class="row">
<div id="Messages" class="red-text" style="width:800px; height:400px; overflow-y:scroll;"></div>
</div>
<script>
$(document).ready(function(){
var namespace='/runPSATask';
var url = 'http://' + document.domain + ':' + location.port + namespace;
var socket = io.connect(url);
socket.on('connect', function() {
socket.emit('join_room');
});
socket.on('msg' , function(data) {
$("#Messages").prepend('<li>'+data.msg+'</li>');
});
socket.on('status', function(data) {
////alert('socket on status ='+ data.msg);
if (data.msg == 'End') {
$("#runTask").attr("disabled",false);
};
});
});
</script>
<script>
$("#runTask").click(function(e) {
$("#runTask").attr("disabled",true);
$("#Messages").empty();
$.ajax({ type: "Post"
, url: '/runPSATask'
, data: $("#runTaskForm").serialize()
, success: function(data) {
$("#Messages").empty();
$("#Messages").prepend('<li>The Task ' + data.taskid + ' has been submitted and will execute in ' + data.duration + ' seconds. </li>');
}
});
e.preventDefault();
console.log('runPSATask complete');
});
</script>
</body>
</html>
このテンプレートには次のものが含まれます。
runTask
ルートを使用してサーバーにタスクを送信するボタン/runSyncTask
。div
idでに配置されMessages
ます。次に、app_sync.py
Flaskアプリケーションを含むというプログラムを作成し、このプログラム内に2つのルートを定義します。
"/"
Webページをレンダリングします(index.html
)"/runSyncTask"
1〜20秒の乱数を生成し、反復ごとに1秒間スリープするループを実行する、長時間実行中のタスクをシミュレートします。#app_sync.py
from flask import render_template, jsonify
from random import randint
from init import app
import tasks
#Render the predefined template index.html
@app.route("/",methods=['GET'])
def index():
return render_template('index.html')
#Defining the route for running A Synchronous Task
@app.route("/runSyncTask",methods=['POST'])
def long_sync_task():
print("Running","/runSyncTask")
#Generate a random number between MIN_WAIT_TIME and MAX_WAIT_TIME
n = randint(app.config['MIN_WAIT_TIME'],app.config['MAX_WAIT_TIME'])
#Call the function long_sync_task included within tasks.py
task = tasks.long_sync_task(n=n)
#Return the random wait time generated
return jsonify({ 'waittime': n })
if __name__ == "__main__":
app.run(debug=True)
このチュートリアルで定義されているすべてのタスクのコアロジックは、プログラム内にありますtasks.py
。
#tasks.py
import time
from celery import Celery
from celery.utils.log import get_task_logger
from flask_socketio import SocketIO
import config
# Setup the logger (compatible with celery version 4)
logger = get_task_logger(__name__)
# Setup the celery client
celery = Celery(__name__)
# Load celery configurations from celeryconfig.py
celery.config_from_object("celeryconfig")
# Setup and connect the socket instance to Redis Server
socketio = SocketIO(message_queue=config.BROKER_URL)
###############################################################################
def long_sync_task(n):
print(f"This task will take {n} seconds.")
for i in range(n):
print(f"i = {i}")
time.sleep(1)
###############################################################################
@celery.task(name = 'tasks.long_async_task')
def long_async_task(n,session):
print(f"The task of session {session} will take {n} seconds.")
for i in range(n):
print(f"i = {i}")
time.sleep(1)
###############################################################################
def send_message(event, namespace, room, message):
print("Message = ", message)
socketio.emit(event, {'msg': message}, namespace=namespace, room=room)
@celery.task(name = 'tasks.long_async_taskf')
def long_async_taskf(data):
room = data['sessionid']
namespace = data['namespase']
n = data['waittime']
#Send messages signaling the lifecycle of the task
send_message('status', namespace, room, 'Begin')
send_message('msg', namespace, room, 'Begin Task {}'.format(long_async_taskf.request.id))
send_message('msg', namespace, room, 'This task will take {} seconds'.format(n))
print(f"This task will take {n} seconds.")
for i in range(n):
msg = f"{i}"
send_message('msg', namespace, room, msg )
time.sleep(1)
send_message('msg', namespace, room, 'End Task {}'.format(long_async_taskf.request.id))
send_message('status', namespace, room, 'End')
###############################################################################
@celery.task(name = 'tasks.long_async_sch_task')
def long_async_sch_task(data):
room = data['sessionid']
namespace = data['namespase']
n = data['waittime']
send_message('status', namespace, room, 'Begin')
send_message('msg' , namespace, room, 'Begin Task {}'.format(long_async_sch_task.request.id))
send_message('msg' , namespace, room, 'This task will take {} seconds'.format(n))
print(f"This task will take {n} seconds.")
for i in range(n):
msg = f"{i}"
send_message('msg', namespace, room, msg )
time.sleep(1)
send_message('msg' , namespace, room, 'End Task {}'.format(long_async_sch_task.request.id))
send_message('status', namespace, room, 'End')
###############################################################################
このセクションでは、このlong_sync_task()
関数を同期タスクとしてのみ使用します。
app_sync.py
プログラムを実行して、同期シナリオをテストしてみましょう。
$ python app_sync.py
http://localhost:5000
Flaskインスタンスが実行されているリンクにアクセスすると、次の出力が表示されます。
「同期タスクの実行」ボタンを押して、プロセスが完了するまで待ちます。
完了すると、トリガーされたタスクに割り当てられたランダムな時間を通知するメッセージが表示されます。
同時に、サーバーがタスクを実行すると、コンソールに1秒ごとに増分された数値が表示されます。
このセクションでは、クライアントがポーリングメカニズムを使用してサーバー側プロセスのフィードバックを要求する可能性のある非同期サービス呼び出しを示します。
簡単に言うと、非同期とは、プログラムが特定のプロセスが完了するのを待たずに、関係なく続行することを意味します。
発信者が開始サービスコールが、やるES N結果をオト待ち時間。発信者は、結果を気にせずにすぐに作業を続行します。発信者が結果に関心がある場合は、後で説明するメカニズムがあります。
最も単純な非同期メッセージ交換パターンはファイアアンドフォーゲットと呼ばれ 、メッセージは送信されますがフィードバックは必要ありませんが、フィードバックが必要な場合、クライアントはポーリングメカニズムを介して結果を繰り返し要求することがあります 。
ポーリングは潜在的に高いネットワーク負荷を引き起こすため、お勧めしません。それでも、サービスプロバイダー(サーバー)がクライアントについて知る必要がないという利点があります。
次の図は、シナリオを示しています。
非同期通信は、イベントに応答する必要のあるコード(たとえば、待機を伴う時間のかかるI / Oバウンド操作)に適しています。
非同期性を選択すると、システムは同時により多くの要求を処理できるようになり、スループットが向上します。
それでは、コーディングに移りましょう。構成ファイルを使用して、セロリの初期化パラメーターを定義しますceleryconfig.py
。
#celeryconfig.py
#Celery Configuration parameters
#Map to Redis server
broker_url = 'redis://localhost:6379/0'
#Backend used to store the tasks results
result_backend = 'redis://localhost:6379/0'
#A string identifying the default serialization to use Default json
task_serializer = 'json'
result_serializer = 'json'
accept_content = ['json']
#When set to false the local system timezone is used.
enable_utc = False
#To track the started state of a task, we should explicitly enable it
task_track_started = True
#Configure Celery to use a specific time zone.
#The timezone value can be any time zone supported by the pytz library
#timezone = 'Asia/Beirut'
#enable_utc = True
Flaskがレンダリングできるテンプレートを作成します(index1.html
):
<!DOCTYPE html>
<html>
<head>
<title>Synchronicity versus Asynchronicity</title>
<link rel="stylesheet" href="{{url_for('static',filename='css/materialize.min.css')}}">
<script src="{{ url_for('static',filename='js/jquery.min.js') }}"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body class="container">
<div class="row">
<h4>Click to start an ansycnhronous task</h4>
</div>
<div class="card-panel">
<form method='post' id="runTaskForm" action="/runAsyncTask">
<button style="height:50px;width:400px" type="submit" id="runTask">Run An Asynchronous Task</button>
</form>
<form method='post' id="getTaskResultForm" action="/getAsyncTaskResult">
<button style="height:50px;width:400px" type="submit" id="getTaskResult">Get Asynchronous Task Result</button>
</form>
</div>
<div class="row">
<div id="Messages" class="red-text" style="width:800px; height:400px; overflow-y:scroll;"></div>
</div>
<script>
$("#runTask").click(function(e) {
$("#runTask").attr("disabled",true);
$("*").css("cursor","wait");
$("#Messages").empty();
$.ajax({ type: "Post"
, url: '/runAsyncTask'
, data: $("#runTaskForm").serialize()
, success: function(data) {
$("#runTask").attr("disabled",false);
$("*").css("cursor","");
$("#Messages").append('The task ' + data.taskid + ' will be executed in asynchronous manner for ' + data.waittime + ' seconds...');
}
});
e.preventDefault();
console.log('runAsyncTask complete');
});
$("#getTaskResult").click(function(e) {
var msg = $("#Messages").text();
var taskid = msg.match("task(.*)will");
//Get The Task ID from The Messages div and create a Target URL
var vurl = '/getAsyncTaskResult?taskid=' + jQuery.trim(taskid[1]);
$.ajax({ type: "Post"
, url: vurl
, data: $("#getTaskResultForm").serialize()
, success: function(data) {
$("*").css("cursor","");
$("#Messages").append('<p> The Status of the task = ' + data.taskid + ' is ' + data.taskstatus + '</p>');
}
});
e.preventDefault();
console.log('getAsyncTaskResult complete');
});
</script>
</body>
</html>
次に、app_async1.py
Flaskアプリを含むプログラムを作成します。
#app_async1.py
from flask import render_template, jsonify, session,request
from random import randint
import uuid
import tasks
from init import app
from celery.result import AsyncResult
@app.route("/",methods=['GET'])
def index():
# create a unique ID to assign for the asynchronous task
if 'uid' not in session:
sid = str(uuid.uuid4())
session['uid'] = sid
print("Session ID stored =", sid)
return render_template('index1.html')
#Run an Asynchronous Task
@app.route("/runAsyncTask",methods=['POST'])
def long_async_task():
print("Running", "/runAsyncTask")
#Generate a random number between MIN_WAIT_TIME and MAX_WAIT_TIME
n = randint(app.config['MIN_WAIT_TIME'],app.config['MAX_WAIT_TIME'])
sid = str(session['uid'])
task = tasks.long_async_task.delay(n=n,session=sid)
#print('taskid',task.id,'sessionid',sid,'waittime',n )
return jsonify({'taskid':task.id,'sessionid':sid,'waittime':n })
#Get The Result of The Asynchronous Task
@app.route('/getAsyncTaskResult', methods=['GET', 'POST'])
def result():
task_id = request.args.get('taskid')
# grab the AsyncResult
result = AsyncResult(task_id)
# print the task id
print("Task ID = ", result.task_id)
# print the Asynchronous result status
print("Task Status = ", result.status)
return jsonify({'taskid': result.task_id, 'taskstatus': result.status})
if __name__ == "__main__":
app.run(debug=True)
このプログラムには、3つの主要なルートがあります。
"/"
:Webページをレンダリングします(index1.html
)。"/runAsyncTask"
:1〜20秒の乱数を生成する非同期タスクを呼び出してから、反復ごとに1秒間スリープするループを実行します。"/getAsyncTaskResult"
:受信したタスクIDに基づいて、タスクの状態を収集します。注意:このシナリオには、SocketIOコンポーネントは含まれていません。
このシナリオをテストして、次の手順に従ってパスを進めましょう。
redis-server.exe
ます。デフォルトのインストールまたはLinux / MacOSの場合は、RedisインスタンスがTCPポート6379で実行されていることを確認してください。$ async-venv\Scripts\celery.exe worker -A tasks --loglevel=DEBUG --concurrency=1 -P solo -f celery.logs
Linux / MacOSでは、非常によく似ています。
$ async-venv/bin/celery worker -A tasks --loglevel=DEBUG --concurrency=1 -P solo -f celery.logs
これasync-venv
が仮想環境の名前であることに注意してください。別の名前を付けた場合は、必ず自分の名前に置き換えてください。セロリが始まると、次の出力が表示されます。
プログラムで定義されたタスクtasks.py
がCeleryに反映されていることを確認してください。
$ python app_async1.py
次に、ブラウザを開いて、次のリンクにアクセスします。
[非同期タスクの実行]ボタンを押すと、新しいタスクがキューに入れられ、直接実行されます。「メッセージ」セクションに、タスクのIDとその実行時間を示すメッセージが表示されます。
[非同期タスク結果の取得]ボタンを(継続的に)押すと、その特定の時間におけるタスクの状態が収集されます。
PENDING
:実行を待機しています。STARTED
:タスクが開始されました。SUCCESS
:タスクは正常に実行されました。FAILURE
:タスクの実行により例外が発生しました。RETRY
:タスクは再試行されています。REVOKED
:タスクが取り消されました。ログファイルに含まれているセロリワーカーのログcelery.logs
を確認すると、タスクのライフサイクルに気付くでしょう。
以前のシナリオに基づいて、タスクの状態を収集するために複数のリクエストを開始することによる煩わしさを軽減するために、サーバーがタスクの状態に関してクライアントを継続的に更新できるようにするソケットテクノロジの組み込みを試みます。
実際、ソケットIOエンジンは、リアルタイムの双方向イベントベースの通信を可能にします。
これがもたらす主な利点は、ネットワークの負荷を軽減し、膨大な数のクライアントに情報を伝達するためにより効率的になることです。
次の図は、シナリオを示しています。
さらに掘り下げる前に、実行する手順について簡単に説明します。
セロリからWebブラウザーにメッセージを送り返すことができるようにするために、以下を活用します。
データ接続を効果的に管理するために、次の区分化戦略を採用します。
"/runAsyncTaskF"
このシナリオに名前空間を割り当てます。(名前空間は、単一の共有接続を介してサーバーロジックを分離するために使用されます)。それでは、コーディングに移りましょう。
index2.html
):<!DOCTYPE html>
<html>
<head>
<title>Synchronicity versus Asynchronicity</title>
<link rel="stylesheet" href="{{url_for('static',filename='css/materialize.min.css')}}">
<script src="{{ url_for('static',filename='js/jquery.min.js') }}"></script>
<script src="{{ url_for('static',filename='js/socket.io.js') }}"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body class="container">
<div class="row">
<h5>Click to start an ansycnhronous task with automatic feedback.</h5>
</div>
<div class="card-panel">
<form method='post' id="runTaskForm" action="/runAsyncTask">
<button style="height:50px;width:400px" type="submit" id="runTask">Run An Asynchronous Task With Automatic Feedback</button>
</form>
</div>
<div class="row">
<div id="Messages" class="red-text" style="width:800px; height:400px; overflow-y:scroll;"></div>
</div>
<script>
$(document).ready(function() {
var namespace = '/runAsyncTaskF';
var url = 'http://' + document.domain + ':' + location.port + namespace;
var socket = io.connect(url);
socket.on('connect', function() {
////alert('socket on connect');
socket.emit('join_room');
});
socket.on('msg', function(data) {
////alert('socket on msg ='+ data.msg);
$("#Messages").prepend('<li>' + data.msg + '</li>');
});
socket.on('status', function(data) {
////alert('socket on status ='+ data.msg);
if (data.msg == 'End') {
$("#runTask").attr("disabled", false);
};
});
});
</script>
<script>
$("#runTask").click(function(e) {
$("#runTask").attr("disabled", true);
$("*").css("cursor", "wait");
$("#Messages").empty();
$.ajax({
type: "Post",
url: '/runAsyncTaskF',
data: $("#runTaskForm").serialize(),
success: function(data) {
$("*").css("cursor", "");
$("#Messages").empty();
$("#Messages").prepend('<li>The Task ' + data.taskid + ' has been submitted. </li>');
}
});
e.preventDefault();
console.log('runAsyncTaskF complete');
});
</script>
</body>
</html>
app_async2.py
Flaskアプリケーションを含むと呼ばれるプログラムを作成します。
#Gevent is a coroutine based concurrency library for Python
from gevent import monkey
#For dynamic modifications of a class or module
monkey.patch_all()
from flask import render_template, jsonify, session, request
from random import randint
import uuid
import tasks
from init import app, socketio
from flask_socketio import join_room
@app.route("/",methods=['GET'])
def index():
# create a unique session ID and store it within the Flask session
if 'uid' not in session:
sid = str(uuid.uuid4())
session['uid'] = sid
print("Session ID stored =", sid)
return render_template('index2.html')
#Run an Asynchronous Task With Automatic Feedback
@app.route("/runAsyncTaskF",methods=['POST'])
def long_async_taskf():
print("Running", "/runAsyncTaskF")
# Generate a random number between MIN_WAIT_TIME and MAX_WAIT_TIME
n = randint(app.config['MIN_WAIT_TIME'], app.config['MAX_WAIT_TIME'])
data = {}
data['sessionid'] = str(session['uid'])
data['waittime'] = n
data['namespase'] = '/runAsyncTaskF'
task = tasks.long_async_taskf.delay(data)
return jsonify({ 'taskid':task.id
,'sessionid':data['sessionid']
,'waittime':data['waittime']
,'namespace':data['namespase']
})
@socketio.on('connect', namespace='/runAsyncTaskF')
def socket_connect():
#Display message upon connecting to the namespace
print('Client Connected To NameSpace /runAsyncTaskF - ',request.sid)
@socketio.on('disconnect', namespace='/runAsyncTaskF')
def socket_connect():
# Display message upon disconnecting from the namespace
print('Client disconnected From NameSpace /runAsyncTaskF - ',request.sid)
@socketio.on('join_room', namespace='/runAsyncTaskF')
def on_room():
room = str(session['uid'])
# Display message upon joining a room specific to the session previously stored.
print(f"Socket joining room {room}")
join_room(room)
@socketio.on_error_default
def error_handler(e):
# Display message on error.
print(f"socket error: {e}, {str(request.event)}")
if __name__ == "__main__":
# Run the application with socketio integration.
socketio.run(app,debug=True)
このプログラムには、主に2つのルートがあります。
"/"
:Webページをレンダリングします(index2.html
)。"/runAsyncTaskF"
:以下を実行する非同期タスクを呼び出します。long_async_taskf()
プログラム内のそれぞれのタスクを呼び出しますtasks.py
。このシナリオを実行するには:
app_async2.py
ブラウザを開き、次のリンクにアクセスしてボタンを押すと、次のような出力が徐々に表示されます。
同時に、コンソールに次の出力が表示されます。
また
celery.logs
、タスクのライフサイクルについてファイルをいつでも確認できます。
このシナリオはシナリオ3に似ています。唯一の違いは、非同期タスクを直接実行する代わりに、このタスクがクライアントによって指定された特定の期間の後に実行されるようにスケジュールされることです。
コーディングに進みましょう。非同期タスクを実行する前に待機する時間を秒単位で表すindex3.html
新しいフィールドを使用してテンプレートを作成し"Duration"
ます。
<!DOCTYPE html>
<html>
<head>
<title>Synchronicity versus Asynchronicity</title>
<link rel="stylesheet" href="{{url_for('static',filename='css/materialize.min.css')}}">
<script src="{{ url_for('static',filename='js/jquery.min.js') }}"></script>
<script src="{{ url_for('static',filename='js/socket.io.js') }}"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body class="container">
<div class="row">
<h5>Click to start a post scheduled ansycnhronous task with automatic feedback.</h5>
</div>
<div class="card-panel">
<form method='post' id="runTaskForm" action="/runPSATask">
<div>
<input id="duration" name="duration" placeholder="Enter duration in seconds. for example: 30" type="text">
<label for="duration">Duration</label>
</div>
<button style="height:50px;width:600px" type="submit" id="runTask">Run A Post Scheduled Asynchronous Task With Automatic Feedback</button>
</form>
</div>
<div class="row">
<div id="Messages" class="red-text" style="width:800px; height:400px; overflow-y:scroll;"></div>
</div>
<script>
$(document).ready(function() {
var namespace = '/runPSATask';
var url = 'http://' + document.domain + ':' + location.port + namespace;
var socket = io.connect(url);
socket.on('connect', function() {
socket.emit('join_room');
});
socket.on('msg', function(data) {
$("#Messages").prepend('<li>' + data.msg + '</li>');
});
socket.on('status', function(data) {
////alert('socket on status ='+ data.msg);
if (data.msg == 'End') {
$("#runTask").attr("disabled", false);
};
});
});
</script>
<script>
$("#runTask").click(function(e) {
$("#runTask").attr("disabled", true);
$("#Messages").empty();
$.ajax({
type: "Post",
url: '/runPSATask',
data: $("#runTaskForm").serialize(),
success: function(data) {
$("#Messages").empty();
$("#Messages").prepend('<li>The Task ' + data.taskid + ' has been submitted and will execute in ' + data.duration + ' seconds. </li>');
}
});
e.preventDefault();
console.log('runPSATask complete');
});
</script>
</body>
</html>
次に、app_async3.py
このシナリオのFlaskアプリは次のとおりです。
#app_async3.py
from gevent import monkey
monkey.patch_all()
from flask import render_template, jsonify, session, request
from random import randint
import uuid
import tasks
from init import app, socketio
from flask_socketio import join_room
@app.route("/",methods=['GET'])
def index():
# create a unique session ID
if 'uid' not in session:
sid = str(uuid.uuid4())
session['uid'] = sid
print("Session ID stored =", sid)
return render_template('index3.html')
#Run a Post Scheduled Asynchronous Task With Automatic Feedback
@app.route("/runPSATask",methods=['POST'])
def long_async_sch_task():
print("Running", "/runPSATask")
# Generate a random number between MIN_WAIT_TIME and MAX_WAIT_TIME
n = randint(app.config['MIN_WAIT_TIME'], app.config['MAX_WAIT_TIME'])
data = {}
data['sessionid'] = str(session['uid'])
data['waittime'] = n
data['namespase'] = '/runPSATask'
data['duration'] = int(request.form['duration'])
#Countdown represents the duration to wait in seconds before running the task
task = tasks.long_async_sch_task.apply_async(args=[data],countdown=data['duration'])
return jsonify({ 'taskid':task.id
,'sessionid':data['sessionid']
,'waittime': data['waittime']
,'namespace':data['namespase']
,'duration':data['duration']
})
@socketio.on('connect', namespace='/runPSATask')
def socket_connect():
print('Client Connected To NameSpace /runPSATask - ',request.sid)
@socketio.on('disconnect', namespace='/runPSATask')
def socket_connect():
print('Client disconnected From NameSpace /runPSATask - ',request.sid)
@socketio.on('join_room', namespace='/runPSATask')
def on_room():
room = str(session['uid'])
print(f"Socket joining room {room}")
join_room(room)
@socketio.on_error_default
def error_handler(e):
print(f"socket error: {e}, {str(request.event)}")
if __name__ == "__main__":
socketio.run(app,debug=True)
今回long_async_sch_task()
からタスクメソッドを使用していることに注意してくださいtasks.py
。
app_async3.py
以前と同じように実行し、ブラウザを開きます。
期間(つまり10)を入力し、ボタンを押して、スケジュール後の非同期タスクを作成します。作成されると、タスクの詳細を示すメッセージが[メッセージ]ボックスに表示されます。
期間フィールドで指定した時間待つ必要があります。タスクが実行されていることがわかります。
また、
celery.logs
ログファイルに含まれているセロリワーカーのログを復元すると、タスクのライフサイクルに気付くでしょう。
セロリタスクをより適切に監視するために、セロリ クラスターを監視および管理するためのWebベースのツールであるFlowerをインストールできます。
注意:フラワーライブラリはの一部でしたrequirements.txt
。
花を使用してセロリのタスクを表示するには、次の手順に従ってください。
$ async-venv\Scripts\flower.exe worker -A tasks --port=5555
Linux / MacOSの場合:
$ async-venv/bin/flower worker -A tasks --port=5555
コンソールに次の情報が表示されます。
アプリに戻ってタスクを実行し、ブラウザを開いて
http://localhost:5555
[タスク]タブに移動します。
タスクが完了すると、フラワーダッシュボードに次のように表示されます。
この記事が、Celeryの助けを借りて同期および非同期リクエストの概念的な基礎を得るのに役立つことを願っています。同期要求は遅くなる可能性があり、非同期要求は迅速に実行されますが、あらゆるシナリオに適切な方法を認識することが重要です。時には、彼らは一緒に働くことさえあります。
リンク: https://www.thepythoncode.com/article/async-tasks-with-celery-redis-and-flask-in-python
1612506216
The document is non-binding. Some information may be outdated as we keep evolving.
BUILD Finance is a decentralised autonomous venture builder, owned and controlled by the community. BUILD Finance produces, funds, and manages community-owned DeFi products.
There are five core activities in which the venture BUILDers engage:
BUILD operates a shared capabilities model, where the DAO provides the backbone support and ensures inter-entity synergies so that the product companies can focus on their own outcomes.
BUILD takes care of all organisational, hiring, back/mid office functions, and the product companies focus on what they can do best, until such time where any individual product outgrows the DAO and becomes fully self-sustainable. At that point, the chick is strong enough to leave the nest and live its own life. The survival of the fittest. No product entity is held within DAO by force.
Along the way, BUILD utilises the investment banking model, which, in its essence, is a process of creating assets, gearing them up, and then flipping them into a fund or setting them as income-generating business systems, all this while taking fees along the way at each step. BUILD heavily focuses on integrating each asset/product with each other to boost productive yield and revenues. For example, BUILD’s OTC Market may be integrated with Metric Exchange to connect the liquidity pools with the trading traffic. The net result – pure synergy that benefits each party involved, acting in a self-reinforcing manner.
BUILD is a hive and is always alive. While some members may appear more active than others, there’s no central source of control or “core teams” as such. BUILD is work in progress where everyone is encouraged to contribute.
Following the natural free market forces, BUILD only works on those products that members are wanting to work on themselves and that they believe have economic value. Effectively, every builder is also a user of BUILD’s products. We are DeFi users that fill the gaps in the ecosystem. Any member can contribute from both purely altruistic or ultra-mercantile intentions – it’s up to the wider community to decide what is deemed valuable and what product to support. The BUILD community is a sovereign individual that votes with their money and feet.
BUILD members = BUILD users. It’s that simple.
$BUILD token is used as a governance token for the DAO. It also represents a pro-rata claim of ownership on all DAO’s assets and liabilities (e.g. BUILD Treasury and $bCRED debt token).
The token was distributed via liquidity mining with no pre-sale and zero founder/private allocation. The farming event lasted for 7 days around mid-Sep 2020. At the time, BUILD didn’t have any products and held no value. Arguably, $BUILD has still zero value as it is not a legal instrument and does not guarantee or promise any returns to anyone. See the launch announcement here https://medium.com/@BUILD_Finance/announcing-build-finance-dc08df585e57
Initial supply was 100,000 $BUILD with 100% distributed via fair launch. Subsequently, the DAO unanimously voted to approve minting of extra 30,000 $BUILD and allocate them as:
For the proposal of the above see: https://forum.letsbuild.finance/t/proposal-2-fund-the-development-of-defi-lending-platform/24
The voting took place at a later retired web-page https://vote.letsbuild.finance. The governance has since moved to Snapshot (link below). The results of the old proposals are not visible there, however, on-chain voting contract can be see here: https://etherscan.io/address/0xa8621477645f76b06a41c9393ddaf79ddee63daf#readContract
Minting keys are not burnt = $BUILD supply is not fixed as token holders can vote on minting new tokens for specific reasons determined by the token holders. For example, the DAO may mint additional tokens to incentivise usage of its products, which would, in turn, increase the value flow or TVL. Dilution is not in the economic benefit of the token holders, hence any such events has to be considered carefully.
Access to minting function is available via on-chain governance. A safe buffer is established in a form of the contract-enforced 24 hour delay, which should provide a sufficient time for the community to flag. Meaning that before such a transaction could be executed, everyone would be able to act in advance by withdrawing their funds / exiting from BUILD. Any malicious minting would, theoretically, result in an immediate market sell-off of $BUILD, making it economically detrimental to do such an action. This makes it highly improbable that any malicious minting would be performed_._
All components of the BUILD DAO and the control over its have been decentralised:
The BUILD treasury has over $400k that can be managed by on-chain proposals and used in whichever way the community desires. For example, to hire developers. Having a functioning product, enough funds in the treasury and a fully decentralised governance has been a long-term goal since the inception in September 2020, and now it’s finally here.
Current holdings are (might be outdated):
In an early stage, the development will be funded by an allocation of bCRED debt tokens for development expenses. After the first product was built (i.e. Metric Exchange), the DAO sold 5,000 $BUILD for 203,849 $DAI which will now be used for funding of other products or a combination of revenue + a smaller token allocation. This is up to the community to decide.
Contracts are not audited. It’s up to the BUILD community governance to decide how to spend our funds. If the community wants to spend any amount for auditing, a voting proposal can be initiated. As with any decisions and proposals, the cost-benefit analysis must be employed in regards to the economical sense of spending any funds on audit vs developing more products and expanding our revenue streams.
$bCRED is a token that allowed the DAO to reward members for work before the DAO source sufficient funds. Effectively, $bCRED is a promissory note or an IOU to give back $DAI at 1:1 ratio, when the DAO starts generating revenues. Read more about $bCRED here: https://medium.com/@BUILD_Finance/what-is-bcred-b97e4cc75f8c.
Since Discord is our primary coordination mechanism, we need to make effort to keep it focused on producing value. During the launch of METRIC, we’ve doubled our total number of users! This made it very difficult for existing users to explain what BUILD is about to new users and created a lot of confusion.
To help improve the quality of conversations, we’ve introduced a new user role called BUILDer. BUILDers will have write-access to product development channels while everyone else will only be able to read them. This should keep those product changes focused on actual productive conversations and make them more informative.
To increase our collective output as a community, a governance vote introduced an incentivisation mechanism for community contribution, tipping, and other small projects using our unique bCRED token (but may change in the future as required). These tokens are stewarded by active community members — “guardians’’ — who are free to allocate these funds to tip people for proactive work. Current guardians are @Son of Ishtar and @0xdev0, although anyone can propose the tip for anyone else. For more details see Proposal #15.
Hence, Guardians are defined as members of the DAO who are entrusted with a community budget for tipping other members for performing various small tasks.
The other immediate focus right now will be to make good use of our newly available funding and hire several product managers for other projects.
Please note that nothing is here set in stone. Just like any other start-up, we’ll keep experimenting, learning, and evolving. What’s listed here is just our current trajectory but it might change at any point.
SOCIAL MEDIA
Would you like to earn BUILD right now! ☞ CLICK HERE
Top exchanges for token-coin trading. Follow instructions and make unlimited money
☞ Binance ☞ Bittrex ☞ Poloniex ☞ Bitfinex ☞ Huobi
Thank you for reading!
#blockchain #cryptocurrency #build finance #build