Skip to content

Commit

Permalink
Reorganize region for source information
Browse files Browse the repository at this point in the history
  • Loading branch information
blaesus committed Feb 20, 2020
1 parent eb904b0 commit 2fd8f30
Show file tree
Hide file tree
Showing 92 changed files with 2,646 additions and 2,621 deletions.
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
# 中国业余无线电台操作证书考试题库Anki牌组工具
# Amateur Radio Exam Question Pool Toolkit (CN+US)

Parse radio exam question pools of China and the United States and generate JSON and CSVs for Anki import or other programmatic uses.

## 1. 中国业余无线电台操作证书考试题库

根据[官方考试题库文本](http://www.crac.org.cn/News/Detail?ID=1862),产生json和csv(见`/generated`),用于导入Anki形成牌组。

原本题库永远是第一项(即「A」)正确,本工具生成CSV时会打乱题枝顺序,但JSON依照原顺序。

组好的牌组见:https://github.com/blaesus/RadioExamCNDeck/releases

## 开发
## 2. FCC Amateur Radio Exam

Three levels are handled:
- Technical (aka element 2), based on

## Development
```bash
npm install
```

源文件只有一个: `radio.ts`.

## 资料来源
业余无线电台操作证书考试题库电子版文本下载(v171031) - http://www.crac.org.cn/News/Detail?ID=1862
Then edit `radio.ts`.

## 本软件(radio.ts)授权
MIT


361 changes: 361 additions & 0 deletions data/cn/generated/CN-A.csv

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions generated/A.json → data/cn/generated/CN-A.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"level": "A",
"randomSeed": 65,
"level": "CN-A",
"randomSeed": 67,
"items": [
{
"serial": "LK0001",
Expand Down
685 changes: 685 additions & 0 deletions data/cn/generated/CN-B.csv

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions generated/B.json → data/cn/generated/CN-B.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"level": "B",
"randomSeed": 66,
"level": "CN-B",
"randomSeed": 67,
"items": [
{
"serial": "LK0001",
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion generated/C.json → data/cn/generated/CN-C.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"level": "C",
"level": "CN-C",
"randomSeed": 67,
"items": [
{
Expand Down
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
622 changes: 622 additions & 0 deletions data/us/generated/US-Extra.csv

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions generated/E.json → data/us/generated/US-Extra.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"level": "E",
"randomSeed": 69,
"level": "US-Extra",
"randomSeed": 85,
"items": [
{
"serial": "E1A01",
Expand Down
454 changes: 454 additions & 0 deletions data/us/generated/US-General.csv

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions generated/G.json → data/us/generated/US-General.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"level": "G",
"randomSeed": 71,
"level": "US-General",
"randomSeed": 85,
"items": [
{
"serial": "G1A01",
Expand Down
423 changes: 423 additions & 0 deletions data/us/generated/US-Technician.csv

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions generated/T.json → data/us/generated/US-Technician.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"level": "T",
"randomSeed": 84,
"level": "US-Technician",
"randomSeed": 85,
"items": [
{
"serial": "T1A01",
Expand Down
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
361 changes: 0 additions & 361 deletions generated/A.csv

This file was deleted.

685 changes: 0 additions & 685 deletions generated/B.csv

This file was deleted.

622 changes: 0 additions & 622 deletions generated/E.csv

This file was deleted.

454 changes: 0 additions & 454 deletions generated/G.csv

This file was deleted.

423 changes: 0 additions & 423 deletions generated/T.csv

This file was deleted.

136 changes: 77 additions & 59 deletions radio.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,68 @@
import { readFileSync, writeFileSync, existsSync } from "fs";
import { join } from "path";
import { decode } from "iconv-lite";

const sourceRoot = `data`;
const generatedFileSubpath = `generated`;

const SEED_DELTA = 0;

const CSV_DELIMITER = '|';
const CSV_NEWLINE = '\n';

type ExamLevel =
"A" | "B" | "C" // Chinese levels
| "T" | "G" | "E" // American levels
interface SourceFileInfo {
level: string,
regionRoot: string,
filename: string,
encoding: string,
pictureExt: string | null
}

const sourceFileInfoList: SourceFileInfo[] = [
{
level: "CN-A",
regionRoot: "cn",
filename: "radioa.txt",
encoding: "gbk",
pictureExt: null,
},
{
level: "CN-B",
regionRoot: "cn",
filename: "radiob.txt",
encoding: "gbk",
pictureExt: null,
},
{
level: "CN-C",
regionRoot: "cn",
filename: "radioc.txt",
encoding: "gbk",
pictureExt: null,
},
{
level: "US-Technician",
regionRoot: "us",
filename: "radiot.txt",
encoding: "utf-8",
pictureExt: "jpg",
},
{
level: "US-General",
regionRoot: "us",
filename: "radiog.txt",
encoding: "utf-8",
pictureExt: "jpg",
},
{
level: "US-Extra",
regionRoot: "us",
filename: "radioe.txt",
encoding: "utf-8",
pictureExt: "png",
},
];


interface Item {
serial: string,
Expand All @@ -20,7 +74,7 @@ interface Item {
}

interface Suite {
level: ExamLevel,
level: string,
randomSeed: number,
items: Item[],
}
Expand All @@ -47,9 +101,10 @@ function getPrng(seed = 0): RandomGenerator {
}
}

function loadFile(level: ExamLevel): string {
const {filename, encoding} = sourceFileInfo[level];
const content = readFileSync(filename);
function loadSource(sourceInfo: SourceFileInfo): string {
const {regionRoot, filename, encoding} = sourceInfo;
const fullPath = join(sourceRoot, regionRoot, filename)
const content = readFileSync(fullPath);
const text = decode(content, encoding);
const unifiedText = text.replace(/\r\n/g, "\n").replace(/\u001e/g, "-");
return unifiedText;
Expand All @@ -66,41 +121,8 @@ function getDefaultItem(): Item {
}
}

const sourceFileInfo: {[level in ExamLevel]: {filename: string, encoding: string, pictureExt: string | null}} = {
A: {
filename: "data/radioa.txt",
encoding: "gbk",
pictureExt: null,
},
B: {
filename: "data/radiob.txt",
encoding: "gbk",
pictureExt: null,
},
C: {
filename: "data/radioc.txt",
encoding: "gbk",
pictureExt: null,
},
T: {
filename: "data/radiot.txt",
encoding: "utf-8",
pictureExt: "jpg",
},
G: {
filename: "data/radiog.txt",
encoding: "utf-8",
pictureExt: "jpg",
},
E: {
filename: "data/radioe.txt",
encoding: "utf-8",
pictureExt: "png",
},
}

function parse(content: string, level: ExamLevel): Item[] {
if (level === "A" || level === "B" || level === "C") {
function parse(content: string, region: SourceFileInfo['regionRoot']): Item[] {
if (region === "cn") {
return parseCn(content);
}
else {
Expand Down Expand Up @@ -222,7 +244,7 @@ function shuffleBranches(suite: Suite, rng: () => number): void {
}
}

function toCsv(suite: Suite): string {
function toCsv(suite: Suite, pictureExt: string | null): string {

function optionIndexLetter(index: number): string {
return String.fromCharCode('A'.charCodeAt(0) + index)
Expand All @@ -238,8 +260,6 @@ function toCsv(suite: Suite): string {
return `<img src='${picturePath}'/>`
}

const { pictureExt } = sourceFileInfo[suite.level];

const lines = [];
for (const item of suite.items) {
const segments = [
Expand All @@ -262,7 +282,7 @@ function toCsv(suite: Suite): string {
function fixMissingPictureLabels(suite: Suite): void {
for (const item of suite.items) {
if (!item.picture) {
const possiblePath = `data/images/${item.serial}.jpg`
const possiblePath = `${sourceRoot}/cn/images/${item.serial}.jpg`
if (existsSync(possiblePath)) {
item.picture = `${item.serial}.jpg`
console.warn(`Fixing item ${item.serial} missing link to picture`)
Expand All @@ -271,38 +291,36 @@ function fixMissingPictureLabels(suite: Suite): void {
}
}

function generate(level: ExamLevel): void {
function transform(sourceInfo: SourceFileInfo): void {
const { level, regionRoot } = sourceInfo;
console.info(`\nTransforming for level ${level}`)
const fileContent = loadFile(level);
const fileContent = loadSource(sourceInfo);
const seed = level.charCodeAt(0) + SEED_DELTA;
const suite: Suite = {
level,
randomSeed: seed,
items: parse(fileContent, level),
items: parse(fileContent, regionRoot),
};
fixMissingPictureLabels(suite);

const jsonPath = 'generated/' + level + '.json';
const jsonPath = join(sourceRoot, regionRoot, generatedFileSubpath, level + '.json');
console.info(`Exporting JSON to ${jsonPath}`)
writeFileSync(jsonPath, JSON.stringify(suite, null, 4));

const csvPath = 'generated/' + level + '.csv';
const csvPath = join(sourceRoot, regionRoot, generatedFileSubpath, level + '.csv');
const prng = getPrng(suite.randomSeed);
shuffleBranches(suite, () => prng.get());
const csvContent = toCsv(suite);
const csvContent = toCsv(suite, sourceInfo.pictureExt);
console.info(`Exporting CSV to ${csvPath}`)
writeFileSync('generated/' + level + '.csv', csvContent);
writeFileSync(csvPath, csvContent);

console.info(`Done.`)
}

function main() {
generate("A");
generate("B");
generate("C");
generate("T");
generate("G");
generate("E");
for (const info of sourceFileInfoList) {
transform(info);
}
}

main()

0 comments on commit 2fd8f30

Please sign in to comment.