Sfoglia il codice sorgente

merge council report generator into (tokenomics) report generator

Joystream Stats 3 anni fa
parent
commit
4db3f080b8

+ 0 - 5
contributions/tech/council-report-generator/cli/.gitignore

@@ -1,5 +0,0 @@
-.idea/*
-lib/*
-node_modules
-yarn.lock
-report.md

+ 0 - 25
contributions/tech/council-report-generator/cli/package.json

@@ -1,25 +0,0 @@
-{
-  "name": "council-report-generator",
-  "version": "0.1.0",
-  "license": "GPL-3.0-only",
-  "scripts": {
-    "index": "ts-node src/index.ts"
-  },
-  "dependencies": {
-    "@joystream/types": "^0.16.1",
-    "@polkadot/api": "4.2.1",
-    "@polkadot/keyring": "^6.0.5",
-    "@polkadot/types": "4.2.1",
-    "@polkadot/util": "^6.0.5",
-    "@polkadot/util-crypto": "^6.0.5",
-    "@types/bn.js": "^4.11.5",
-    "bn.js": "^4.11.8",
-    "express": "^4.17.1"
-  },
-  "devDependencies": {
-    "@polkadot/ts": "^0.3.62",
-    "@types/node": "^16.4.0",
-    "ts-node": "^10.1.0",
-    "typescript": "^3.9.7"
-  }
-}

+ 0 - 83
contributions/tech/council-report-generator/cli/src/index.ts

@@ -1,83 +0,0 @@
-// @ts-check
-
-import { ApiPromise, WsProvider } from "@polkadot/api";
-import { types as joyTypes } from "@joystream/types";
-import { Hash, Moment } from "@polkadot/types/interfaces";
-import {
-  BlockRange,
-  CouncilMemberInfo,
-  CouncilRoundInfo,
-  ProposalFailedReason,
-  ProposalInfo,
-  ProposalStatus,
-  ProposalType,
-  ReportData,
-} from "./types";
-import { Seats } from "@joystream/types/council";
-import { MemberId, Membership } from "@joystream/types/members";
-import { StorageKey, u32, U32, Vec } from "@polkadot/types";
-import { Mint, MintId } from "@joystream/types/mint";
-import { ProposalDetailsOf, ProposalOf } from "@joystream/types/augment/types";
-
-import { generateReportData } from "./report-functions";
-
-const fsSync = require("fs");
-const fs = fsSync.promises;
-
-const PROPOSAL_URL = "https://testnet.joystream.org/#/proposals/";
-const ELECTION_OFFSET = 1;
-
-async function main() {
-  const args = process.argv.slice(2);
-  if (args.length != 2) {
-    console.error("Usage: [start bock number] [end block number]");
-    process.exit(1);
-  }
-
-  const startCouncilBlock = Number(args[0]);
-  const endCouncilBlock = Number(args[1]);
-
-  const provider = new WsProvider("ws://localhost:9944");
-
-  const api = new ApiPromise({ provider, types: joyTypes });
-  await api.isReady;
-
-  const startHash = (await api.rpc.chain.getBlockHash(
-    startCouncilBlock
-  )) as Hash;
-  const endHash = (await api.rpc.chain.getBlockHash(endCouncilBlock)) as Hash;
-  const blockRange = new BlockRange(
-    startCouncilBlock,
-    startHash,
-    endCouncilBlock,
-    endHash
-  );
-
-  const reportData = await generateReportData(api, blockRange);
-  const reportGenerationResult = await generateReport(reportData);
-  await fs.writeFile("report.md", reportGenerationResult);
-
-  api.disconnect();
-}
-
-async function generateReport(data: ReportData) {
-  try {
-    let fileData = await fs.readFile(__dirname + "/../report-template.md", {
-      encoding: "utf8",
-    });
-
-    let entries = Object.entries(data);
-
-    for (let entry of entries) {
-      let regex = new RegExp("{" + entry[0] + "}", "g");
-      fileData = fileData.replace(regex, entry[1].toString());
-    }
-
-    return fileData;
-  } catch (e) {
-    console.error(e);
-  }
-  return "";
-}
-
-main();

+ 0 - 118
contributions/tech/report-generator/report-template.md.old

@@ -1,118 +0,0 @@
-# Tokenomics + Network Report
-This is a report which explains the current state of the Joystream network in numbers. It pulls figures from the chain and tries to provide a basic level of information about the network, tokens and more. 
-
-## 1.0 Basic Information
-* Date Range: {dateStart} - {dateEnd}
-* Council session #: {councilRound}
-* Starting block: {startBlock}
-* Block range: {startBlock} - {endBlock}
-
-### 1.1 Block Generation Information
-| Property                        | This Session | All Sessions | % Change |
-|---------------------------------|--------------|--------------|----------|
-| Number of blocks                | {newBlocks}       | {endBlock}   |   {percNewBlocks}       |
-| Block generation time (average) | {avgBlockProduction}|              |          |
-| Number of nodes (average)       |                     |              |          |
-
-### 1.2 Token + USD Information
-| Property       | This Session | All Sessions | % Change |
-|----------------|--------------|--------------|----------|
-| Token Issuance | {newIssuance}   | {totalIssuance}| {percNewIssuance}|
-| Token Burn     | {newTokensBurn} |              |          |
-| USD Backing    |                 |              |          |
-
-### 1.3 Membership Information
-| Property          | This Session | All Sessions | % Change |
-|-------------------|--------------|--------------|----------|
-| Number of members |{newMembers}  |{totalMembers}|{percNewMembers}|
-
-## 2.0 Tokenomics
-### 2.1 Token generation breakdown
-| Property                    | This Session | All Sessions | % Change |
-|-----------------------------|--------------|--------------|----------|
-| Total Tokens Minted         |{totalMinted}            |              |          |
-| Validator Role              |{newValidatorReward}|              |          |
-| Storage Role                |{newStorageProviderReward}              |              |          |
-| Council Role                |              |              |          |
-
-### 2.2 Mints 
-| Property                  | This Session | All Sessions | % Change |
-|---------------------------|--------------|--------------|----------|
-| Council Mint Total Minted |{newCouncilMinted}|              |          |
-| Curator Mint Total Minted |{newCuratorMinted}|              |          |
-
-## 3.0 Council
-* Council session #: {councilRound}
-* Number of council members: {councilMembers}
-* Total number of proposals: {newProposals}
-### 3.1 Elections
-| Property                    | This Session | All Sessions | % Change |
-|-----------------------------|--------------|--------------|----------|
-| Total Applicants            |{electionApplicants}      |{electionAvgApplicants}||
-| Total Applicant Stake       |{electionApplicantsStakes}|              |          |
-| Total Votes                 |{electionVotes}           |              |          |
-| Avg Votes per Applicant     |{avgVotePerApplicant}     |              |          |
-
-### 3.2 Proposals
-| Proposal Type                           | # of proposals during this session | Total number of proposal type |
-|-----------------------------------------|------------------------------------|-------------------------------|
-| Text                                    | {newTextProposals}                              |                               |
-| Runtime Upgrade                         | {newRuntimeUpgradeProposal}                    |                               |
-| Set Election Parameters                 | {newSetElectionParametersProposal}              |                               |
-| Spending                                | {newSpendingProposal}                           |                               |
-| Set Lead                                | {newSetLeadProposal}                            |                               |
-| Set Content Working Group Mint Capacity | {newSetContentWorkingGroupMintCapacityProposal} |                               |
-| Evict Storage Provider                  | {newEvictStorageProviderProposal}               |                               |
-| Set Validator Count                     | {newSetValidatorCountProposal}                  |                               |
-| Set Storage Role Parameters             | {newSetStorageRoleParametersProposal}           |                               |
-* Average time for proposal vote success:
-* Average overall time for proposal vote success:
-
-## 4 Roles
-### 4.1 Validator Information
-| Property                    | This Session | All Sessions | % Change |
-|-----------------------------|--------------|--------------|----------|
-| Number of validators        | {avgValidators}             |              |          |
-| Validator total stake       |              |              |          |
-| Average stake per validator |              |              |          |
-| Tokens generated by validator role |{newValidatorReward}            |              |          |
-
-### 4.2 Storage Role
-| Property                | This Session | All Sessions | % Change |
-|-------------------------|--------------|--------------|----------|
-| Number of storage nodes |              |              |          |
-| Content storage size    |              |              |          |
-| Total storage stake     |              |              |          |
-| Average storage stake   |              |              |          |
-| Storage Role Reward (/24h)   |              |              |          |
-
-### 4.3 Curator Role
-| Property                | This Session | All Sessions | % Change |
-|-------------------------|--------------|--------------|----------|
-| Curator roles filled     |              |              |          |
-
-
-## 5.0 User Generated Content
-### 5.1 Media & Uploads
-| Property                | This Session | All Sessions | % Change |
-|-------------------------|--------------|--------------|----------|
-| Number of uploads       | {newMedia}       |{totalMedia} | {percNewMedia} |
-| Size of content         | {newUsedSpace} | {totalUsedSpace} | {percNewUsedSpace} |
-| Average size of content | {avgNewSizePerContent} |  {totalAvgSizePerContent} | {percAvgSizePerContent}|
-| Number of channels      | {newChannels} | {totalChannels} | {percNewChannels} |
-| Avg. uploads per channel      |              |              |          |
-
-### 5.2 Forum Activity
-| Property          | This Session | All Sessions | % Change |
-|-------------------|--------------|--------------|----------|
-| Number of threads | {newThreads} |{totalThreads}| {percNewThreads}|
-| Number of posts   | {newPosts}   |{totalPosts}  | {percNewPosts}|
-
-## 6 Todo / Ideas
-* Video duration
-* KPIs
-* Unique channels
-* Verified channels
-* Censored channels
-* Forum posts by subcategory
-* Total staked across platform

+ 2 - 2
contributions/tech/council-report-generator/cli/src/report-functions.ts → contributions/tech/report-generator/src/council.ts

@@ -8,7 +8,7 @@ import {
   ProposalStatus,
   ProposalType,
   ReportData,
-} from "./types";
+} from "./types/council";
 import { StorageKey, U32, u32, Vec } from "@polkadot/types";
 import { Seats } from "@joystream/types/council";
 import { MemberId, Membership } from "@joystream/types/members";
@@ -296,7 +296,7 @@ async function getProposal(
     id
   )) as ProposalOf;
   if (proposal.createdAt?.toBigInt() < range.startBlockHeight) {
-    return;
+    return null;
   }
 
   let proposalInfo = new ProposalInfo();

+ 101 - 23
contributions/tech/report-generator/src/generator.ts

@@ -1,16 +1,40 @@
 import fs from "fs";
 const exec = require("util").promisify(require("child_process").exec);
-import { StatisticsCollector } from "./StatisticsCollector";
-import { connectApi, getHead, getCouncils } from "./lib/api";
+
+import { ApiPromise } from "@polkadot/api";
+import { connectApi, getHead, getCouncils, getCouncilRound } from "./lib/api";
+import { StatisticsCollector } from "./tokenomics";
+import { generateReportData } from "./council";
+import { Config } from "./types/tokenomics";
+
+// types
 import { Round } from "./lib/types";
-import { Config } from "./types";
+import { types as joyTypes } from "@joystream/types";
+import { Hash, Moment } from "@polkadot/types/interfaces";
+import {
+  BlockRange,
+  CouncilMemberInfo,
+  CouncilRoundInfo,
+  ProposalFailedReason,
+  ProposalInfo,
+  ProposalStatus,
+  ProposalType,
+  ReportData,
+} from "./types/council";
+import { Seats } from "@joystream/types/council";
+import { MemberId, Membership } from "@joystream/types/members";
+import { StorageKey, u32, U32, Vec } from "@polkadot/types";
+import { Mint, MintId } from "@joystream/types/mint";
+import { ProposalDetailsOf, ProposalOf } from "@joystream/types/augment/types";
 
 const CONFIG: Config = {
   repoDir: __dirname + "/../../../../",
   reportsDir: "council/tokenomics",
   spendingCategoriesFile: "governance/spending_proposal_categories.csv",
-  templateFile: __dirname + "/../report-template.md",
+  councilTemplate: __dirname + "/../templates/council.md",
+  tokenomicsTemplate: __dirname + "/../templates/tokenomics.md",
   providerUrl: "ws://127.0.0.1:9944",
+  proposalUrl: "https://testnet.joystream.org/#/proposals/",
   statusUrl: "https://status.joystream.org/status/",
   burnAddress: "5D5PhZQNJzcJXVBxwJxZcsutjKPqUPydrvpu6HeiBfMaeKQu",
   cacheDir: "cache",
@@ -19,31 +43,62 @@ const CONFIG: Config = {
   channelClassId: 1,
 };
 
-async function main(config: Config) {
-  const { templateFile } = config;
-  const args = process.argv.slice(2);
-  if (args.length < 2) return updateReports(config, Number(args[0]));
+const councilReport = async (
+  startBlock: number,
+  endBlock: number,
+  config: Config
+) => {
+  const { repoDir, councilTemplate, providerUrl } = config;
+  const api: ApiPromise = await connectApi(providerUrl);
+  await api.isReady;
 
-  const startBlock = Number(args[0]);
-  const endBlock = Number(args[1]);
+  // council report
+  const startHash: Hash = await api.rpc.chain.getBlockHash(startBlock);
+  const endHash: Hash = await api.rpc.chain.getBlockHash(endBlock);
+  const blockRange = new BlockRange(startBlock, startHash, endBlock, endHash);
 
-  if (isNaN(startBlock) || isNaN(endBlock) || startBlock >= endBlock) {
-    console.error("Invalid block range.");
-    process.exit(1);
-  } else generateReport(startBlock, endBlock, config);
-}
+  const data = await generateReportData(api, blockRange);
+  const report = await generateCouncilReport(data, councilTemplate);
+
+  const term = data.councilTerm;
+  const version = startBlock < 717987 ? "antioch" : "sumer";
+  const versionStr = version[0].toUpperCase() + version.slice(1);
+  const filename = `council/reports/${version}-reports/${versionStr}_Council${term}_Report.md`;
+  fs.writeFileSync(repoDir + filename, report);
+  console.log(`-> Wrote ${filename}`);
+
+  api.disconnect();
+};
+
+const generateCouncilReport = async (
+  data: ReportData,
+  templateFile: string
+) => {
+  try {
+    let fileData = await fs.readFileSync(templateFile, "utf8");
+    let entries = Object.entries(data);
+
+    for (let entry of entries) {
+      let regex = new RegExp("{" + entry[0] + "}", "g");
+      fileData = fileData.replace(regex, entry[1].toString());
+    }
+    return fileData;
+  } catch (e) {
+    console.error(e);
+    return "";
+  }
+};
 
-const generateReport = async (
+const tokenomicsReport = async (
   startBlock: number,
   endBlock: number,
   config: Config
 ): Promise<boolean> => {
-  const { templateFile, repoDir, reportsDir } = config;
-  let fileData = fs.readFileSync(templateFile, "utf8");
+  const { tokenomicsTemplate, repoDir, reportsDir } = config;
+  let fileData = fs.readFileSync(tokenomicsTemplate, "utf8");
   let statsCollecttor = new StatisticsCollector();
   console.log(`-> Collecting stats from ${startBlock} to ${endBlock}`);
   const stats = await statsCollecttor.getStats(startBlock, endBlock, config);
-  console.log(stats);
   if (!stats.dateStart) return false;
   const round = stats.councilRound || 1;
 
@@ -62,9 +117,9 @@ const generateReport = async (
 };
 
 const updateReports = async (config: Config, round?: number) => {
-  const { templateFile, providerUrl } = config;
+  const { providerUrl } = config;
   console.debug(`Connecting to ${providerUrl}`);
-  const api = await connectApi(providerUrl);
+  const api: ApiPromise = await connectApi(providerUrl);
   await api.isReady;
 
   console.log(`-> Fetching councils`);
@@ -74,7 +129,7 @@ const updateReports = async (config: Config, round?: number) => {
     if (round === null || isNaN(round)) {
       console.log(`-> Updating reports`);
       await Promise.all(
-        councils.map(({ start, end }) => generateReport(start, end, config))
+        councils.map(({ start, end }) => createReports(start, end, config))
       );
     } else {
       const council = councils.find((c) => c.round === round);
@@ -82,10 +137,33 @@ const updateReports = async (config: Config, round?: number) => {
       console.log(
         `-> Updating round ${round} (${council.start}-${council.end})`
       );
-      await generateReport(council.start, council.end, config);
+      await createReports(council.start, council.end, config);
     }
     process.exit();
   });
 };
 
+const createReports = (
+  startBlock: number,
+  endBlock: number,
+  config: Config
+) => {
+  councilReport(startBlock, endBlock, config);
+  return tokenomicsReport(startBlock, endBlock, config);
+};
+
+const main = async (config: Config) => {
+  const args = process.argv.slice(2);
+  if (args.length < 2) return updateReports(config, Number(args[0]));
+
+  const startBlock = Number(args[0]);
+  const endBlock = Number(args[1]);
+
+  if (isNaN(startBlock) || isNaN(endBlock) || startBlock >= endBlock) {
+    console.error("Invalid block range.");
+    process.exit(1);
+  }
+
+  createReports(startBlock, endBlock, config);
+};
 main(CONFIG);

+ 1 - 1
contributions/tech/report-generator/src/StatisticsCollector.ts → contributions/tech/report-generator/src/tokenomics.ts

@@ -9,7 +9,7 @@ import {
   EventRecord,
   Hash,
 } from "@polkadot/types/interfaces";
-import { Config, MintStatistics, Statistics, WorkersInfo } from "./types";
+import { Config, MintStatistics, Statistics, WorkersInfo } from "./types/tokenomics";
 import {
   CacheEvent,
   Bounty,

+ 0 - 0
contributions/tech/council-report-generator/cli/src/types.ts → contributions/tech/report-generator/src/types/council.ts


+ 13 - 11
contributions/tech/report-generator/src/types.ts → contributions/tech/report-generator/src/types/tokenomics.ts

@@ -1,15 +1,17 @@
 export interface Config {
-  repoDir: string,
-  reportsDir: string,
-  spendingCategoriesFile: string,
-  templateFile: string,
-  providerUrl: string,
-  statusUrl: string,
-  burnAddress: string,
-  cacheDir: string,
-  councilRoundOffset: number,
-  videoClassId: number,
-  channelClassId: number,
+  repoDir: string;
+  reportsDir: string;
+  spendingCategoriesFile: string;
+  councilTemplate: string;
+  tokenomicsTemplate: string;
+  providerUrl: string;
+  proposalUrl: string;
+  statusUrl: string;
+  burnAddress: string;
+  cacheDir: string;
+  councilRoundOffset: number;
+  videoClassId: number;
+  channelClassId: number;
 }
 
 export class Statistics {

+ 0 - 0
contributions/tech/council-report-generator/cli/report-template.md → contributions/tech/report-generator/templates/council.md


+ 0 - 0
contributions/tech/report-generator/report-template.md → contributions/tech/report-generator/templates/tokenomics.md