提交 f92f0c16 编写于 作者: 幻灰龙's avatar 幻灰龙

Merge branch 'encoding' into 'master'

Encoding

See merge request !3
repos:
- repo: git@codechina.csdn.net:csdn/skill_tree_hook.git
rev: 8cb76ab3b493582d3dab0021fb6fddfa28ec5a66
hooks:
- id: pre-commit
verbose: true
\ No newline at end of file
# 在C#中执行基本字符串格式设置
# 在C#中执行基本字符串格式设置
System.String是一个类,专门用于存储字符串,允许对字符串进行许多操作。由于这种数据类型非常重要,C#提供了它自己的关键字和相关的语法,以便于使用这个类来轻松地处理字符串。 String源码网址: https://referencesource.microsoft.com/#mscorlib/system/string.cs
System.String是一个类,专门用于存储字符串,允许对字符串进行许多操作。由于这种数据类
型非常重要,C#提供了它自己的关键字和相关的语法,以便于使用这个类来轻松地处理字符串。
String 源码网址: https://referencesource.microsoft.com/#mscorlib/system/string.cs
使用运算符重载可以连接字符串:
使用运算符重载可以连接字符串:
```csharp
string message1 = "Hello"; // return "Hello"
......@@ -10,34 +12,34 @@ message1 += ", CodeChina"; // return "Hello, CodeChina"
string message2 = message1 + "!"; //return "Hello, CodeChina!"
```
C#还允许使用类似于索引器的语法来提取指定的字符:
C#还允许使用类似于索引器的语法来提取指定的字符:
```csharp
string message1 = "Hello";
char char1 = message[1] // return 'e'
```
这个类可以完成许多常见的任务,如替换字符、删除空白和把字母变成大写形式等等。可用的方法如下表所示(此表并不完整,只介绍部分字符串提供的常用功能)。
这个类可以完成许多常见的任务,如替换字符、删除空白和把字母变成大写形式等等。可用的方法如下表所示(此表并不完整,只介绍部分字符串提供的常用功能)。
|方法|作用|
|方法|作用|
|----|----|
|Compare|比较字符串的内容,判断某些字符是否相等|
|Concat |把多个字符串实例合并为一个实例|
|Format |格式化包含各种值的字符串和如何格式化每个值的说明符|
|IndexOf|定位字符串中第一次出现某个给定子字符串或字符的位置|
|Insert |把一个字符串实例插入到另一个字符串实例的指定索引处|
|Join |合并字符串数组,创建一个新字符串|
|PadLeft|在字符串的左侧,通过添加指定的重复字符填充字符串|
|Replace|用另一个字符或子字符串替换字符串中给定的字符或子字符串|
|Split |在出现给定字符的地方,把字符串拆分为一个字符串数组|
|Substring|在字符串中检索给定位置的子字符串|
|ToLower|把字符串转换为小写形式|
|ToUpper|把字符串转换为大写形式|
|Trim |删除首位的空白|
>注意如果Format想要输出'{'或'}',请这样string.Format("{{Hello}}");
除了这些方法C#还提供了'@'和'$',@是可以忽略字符串的转义,$是可以把代码插到字符串中。
|Compare|比较字符串的内容,判断某些字符是否相等|
|Concat |把多个字符串实例合并为一个实例|
|Format |格式化包含各种值的字符串和如何格式化每个值的说明符|
|IndexOf|定位字符串中第一次出现某个给定子字符串或字符的位置|
|Insert |把一个字符串实例插入到另一个字符串实例的指定索引处|
|Join |合并字符串数组,创建一个新字符串|
|PadLeft|在字符串的左侧,通过添加指定的重复字符填充字符串|
|Replace|用另一个字符或子字符串替换字符串中给定的字符或子字符串|
|Split |在出现给定字符的地方,把字符串拆分为一个字符串数组|
|Substring|在字符串中检索给定位置的子字符串|
|ToLower|把字符串转换为小写形式|
|ToUpper|把字符串转换为大写形式|
|Trim |删除首位的空白|
>注意如果Format想要输出'{'或'}',请这样string.Format("{{Hello}}");
除了这些方法C#还提供了'@'和'$',@是可以忽略字符串的转义,$是可以把代码插到字符串中。
```csharp
int count = 6;
......@@ -45,9 +47,9 @@ string str1 = $"6{count}6"; // return "666"
string str2 = @"6\r6\n6"; //return "6\r6\n6"
```
以下程序中,不能使str3等于`"Hello, CodeChina!"`的是:
以下程序中,不能使str3等于`"Hello, CodeChina!"`的是:
## 答案
## 答案
```csharp
var str1 = "Hello, ";
......@@ -55,7 +57,7 @@ var str2 = "CodeChina!";
var str3 = "str1" + "str2";
```
## 选项
## 选项
### A
......@@ -81,26 +83,26 @@ var str2 = "CodeChina!";
var str3 = string.Concat(str1, str2);
```
|格式说明符|应用|含义|示例|
|格式说明符|应用|含义|示例|
|----------|----|----|----|
|C|数字类型|特定地区的货币值|$666.00|
|D|用于数字类型|一般的整数|666|
|E|数字类型|科学计数法|6.660000E+002|
|F|数字类型|小数点后的位数固定|666.00|
|G|数字类型|一般的数字|666|
|N|数字类型|通常是特定地区的数字格式|666.00|
|P|数字类型|百分比计数法|66,600.00%|
|X|只用于整数类型|十六进制格式|29A|
|C|数字类型|特定地区的货币值|$666.00|
|D|用于数字类型|一般的整数|666|
|E|数字类型|科学计数法|6.660000E+002|
|F|数字类型|小数点后的位数固定|666.00|
|G|数字类型|一般的数字|666|
|N|数字类型|通常是特定地区的数字格式|666.00|
|P|数字类型|百分比计数法|66,600.00%|
|X|只用于整数类型|十六进制格式|29A|
以下程序中,能让str1等于666.0的是:
以下程序中,能让str1等于666.0的是:
## 答案
## 答案
```csharp
string str1 = string.Format("{0:F1}", 666);
```
## 选项
## 选项
### A
......
# C# 3.0 特性 匿名类型
# C# 3.0 特性 匿名类型
匿名类型提供了一种方便的方法,可以将一组只读属性封装到单个对象中,而无需先显式定义类型。类型名称由编译器生成,在源代码级别不可用。每个属性的类型由编译器推断。
您可以通过将new运算符与对象初始值设定项一起使用来创建匿名类型。
匿名类型提供了一种方便的方法,可以将一组只读属性封装到单个对象中,而无需先显式定义类型。类型名称由编译器生成,在源代码级别不可用。每个属性的类型由编译器推断。
您可以通过将new运算符与对象初始值设定项一起使用来创建匿名类型。
```csharp
var user = new { name = "Gao", id = 996, height = 172.5 }; // 此时编译器会提示匿名类型, 是 new {string name, int id, double height}
var user = new { name = "Gao", id = 996, height = 172.5 }; // 此时编译器会提示匿名类型, 是 new {string name, int id, double height}
Console.WriteLine(string.Format("name {0}, id {1}, height {2}", user.name, user.id, user.height));
```
如果一个类里面的属性和字段太多,想要用LINQ语句查找,可以试试匿名类型。以下代码举个例子,假如有一个examples是example类的集合,然后输出所有的Name,如果直接查询会把example类的所有属性和字段带过来,使用匿名类型会导致查询中返回的数据量较少。
如果一个类里面的属性和字段太多,想要用LINQ语句查找,可以试试匿名类型。以下代码举个例子,假如有一个examples是example类的集合,然后输出所有的Name,如果直接查询会把example类的所有属性和字段带过来,使用匿名类型会导致查询中返回的数据量较少。
```csharp
var result = from example in examples select new { example.Name };
......@@ -19,15 +19,15 @@ foreach (var item in result)
}
```
在下列选项中,可以设置一个name是“csdn”的匿名类型的是:
在下列选项中,可以设置一个name是“csdn”的匿名类型的是:
## 答案
## 答案
```csharp
var csdn = new { name = "csdn" };
```
## 选项
## 选项
### A
......
# C# 3.0 特性 自动实现属性
# C# 3.0 特性 自动实现属性
在 C# 3.0 及更高版本,当属性访问器中不需要任何其他逻辑时,自动实现的属性会使属性声明更加简洁。
在 C# 3.0 及更高版本,当属性访问器中不需要任何其他逻辑时,自动实现的属性会使属性声明更加简洁。
对于字段可以用属性来封装,上面代码的name就是用Name封装,并且使用时使用Name。C# 3.0特性支持了自动实现属性的访问器,这样就可以写成Id这样,也能实现一样的功能。这里的属性将set访问器声明为私有,那就是私有的访问级别,如果访问器只声明get访问器时,除了能在构造函数中可变,在其他任何位置都不可变。
对于字段可以用属性来封装,上面代码的name就是用Name封装,并且使用时使用Name。C# 3.0特性支持了自动实现属性的访问器,这样就可以写成Id这样,也能实现一样的功能。这里的属性将set访问器声明为私有,那就是私有的访问级别,如果访问器只声明get访问器时,除了能在构造函数中可变,在其他任何位置都不可变。
```csharp
public class Example
......@@ -24,15 +24,15 @@ public class Example
}
```
如上代码,在下列选项中,不可以设置属性值的是:
如上代码,在下列选项中,不可以设置属性值的是:
## 答案
## 答案
```csharp
Example example = new Example(){UserId = Guid.NewGuid().ToString()};
```
## 选项
## 选项
### A
......
# C# 3.0 特性 表达式树
# C# 3.0 特性 表达式树
表达式树是定义代码的数据结构。 它们基于编译器用于分析代码和生成已编译输出的相同结构。
以下代码创建了一个表达式并且执行了,Compile方法可以编译表达式树由描述为可执行代码的 lambda 表达式,并生成一个委托,表示 lambda 表达式。
注意这里不能使用var来声明此表达式树,因为此操作无法执行,这是由于赋值右侧是隐式类型而导致的。
表达式树是定义代码的数据结构。 它们基于编译器用于分析代码和生成已编译输出的相同结构。
以下代码创建了一个表达式并且执行了,Compile方法可以编译表达式树由描述为可执行代码的 lambda 表达式,并生成一个委托,表示 lambda 表达式。
注意这里不能使用var来声明此表达式树,因为此操作无法执行,这是由于赋值右侧是隐式类型而导致的。
```csharp
Expression<Func<int>> add = () => 1 + 2;
var func = add.Compile(); // 创建一个 delegate
var answer = func(); // 调用 delegate, 得到值3
var func = add.Compile(); // 创建一个 delegate
var answer = func(); // 调用 delegate, 得到值3
Console.WriteLine(answer);
```
根据上面例子,下列选项中,可以生成一个1+2的表达式的是:
根据上面例子,下列选项中,可以生成一个1+2的表达式的是:
## 答案
## 答案
```csharp
var one = Expression.Constant(1, typeof(int));
......@@ -21,7 +21,7 @@ var two = Expression.Constant(2, typeof(int));
var addition = Expression.Add(one, two);
```
## 选项
## 选项
### A
......
# C# 3.0 特性 扩展方法
# C# 3.0 特性 扩展方法
扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。
扩展方法是一种静态方法,但可以像扩展类型上的实例方法一样进行调用。调用扩展方法与调用在类型中定义的方法没有明显区别。
扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。
扩展方法是一种静态方法,但可以像扩展类型上的实例方法一样进行调用。调用扩展方法与调用在类型中定义的方法没有明显区别。
在下列选项中,是扩展方法并实现了判断字符串中是否包含Q或q的代码的是:
在下列选项中,是扩展方法并实现了判断字符串中是否包含Q或q的代码的是:
## 答案
## 答案
```csharp
public static class Extensions
......@@ -25,7 +25,7 @@ public static class Extensions
}
```
## 选项
## 选项
### A
......
# C# 3.0 特性 Lambda 表达式
# C# 3.0 特性 Lambda 表达式
Lambda表达式可采用表达式为其主体,也可以采用语句块作为其主体:
Lambda表达式可采用表达式为其主体,也可以采用语句块作为其主体:
```csharp
(input-parameters) => expression
(input-parameters) => { <sequence-of-statements> }
```
Lambda表达式可以没有参数,如果有参数需要在左侧添加,任何 Lambda 表达式都可以转换为委托类型。
Lambda表达式可以没有参数,如果有参数需要在左侧添加,任何 Lambda 表达式都可以转换为委托类型。
在下列选项中,没有编译错误并且可以输出5的平方的是:
在下列选项中,没有编译错误并且可以输出5的平方的是:
## 答案
## 答案
```csharp
Func<int, int> square = x => x * x;
Console.WriteLine(square(5));
```
## 选项
## 选项
### A
......
# C# 3.0 特性 查询表达式
# C# 3.0 特性 查询表达式
查询表达式必须以from子句开头。 它指定数据源以及范围变量。查询表达式以select或group结尾。
查询表达式必须以from子句开头。 它指定数据源以及范围变量。查询表达式以select或group结尾。
* 使用 where 子句可基于一个或多个谓词表达式,从源数据中筛选出元素。
* 使用 select 子句可生成所有其他类型的序列。
* 使用 group 子句可生成按指定键组织的组的序列。
* 使用 into 关键字可以在 select 或 group 子句中创建存储查询的临时标识符。
* 使用 orderby 子句可按升序或降序对结果进行排序。
* 使用 join 子句可基于每个元素中指定的键之间的相等比较,将一个数据源中的元素与另一个数据源中的元素进行关联和/或合并。
* 使用 let 子句可将表达式(如方法调用)的结果存储在新范围变量中。
* 使用 where 子句可基于一个或多个谓词表达式,从源数据中筛选出元素。
* 使用 select 子句可生成所有其他类型的序列。
* 使用 group 子句可生成按指定键组织的组的序列。
* 使用 into 关键字可以在 select 或 group 子句中创建存储查询的临时标识符。
* 使用 orderby 子句可按升序或降序对结果进行排序。
* 使用 join 子句可基于每个元素中指定的键之间的相等比较,将一个数据源中的元素与另一个数据源中的元素进行关联和/或合并。
* 使用 let 子句可将表达式(如方法调用)的结果存储在新范围变量中。
在下列选项中,可以将examples集合中所有Count大于10的子项目按照Count从大到小的顺序格式化显示的是:
在下列选项中,可以将examples集合中所有Count大于10的子项目按照Count从大到小的顺序格式化显示的是:
## 答案
## 答案
```csharp
from example in examples
......@@ -21,7 +21,7 @@ orderby example.Count descending
select $"{example.Name}\t{example.Count}";
```
## 选项
## 选项
### A
......
......@@ -9,7 +9,14 @@
"扩展方法"
],
"children": [],
"export": [],
"export": [
"AnonymousType.json",
"LambdaExpression.json",
"AutoImplementedProperties.json",
"ExtensionMethod.json",
"ExpressionTree.json",
"QueryExpression.json"
],
"keywords_must": [],
"keywords_forbid": []
}
\ No newline at end of file
# Web API应用
# Web API应用
应用程序接口(API,Application Programming Interface)是基于编程语言构建的结构,使开发人员更容易地创建复杂的功能。它们抽象了复杂的代码,并提供一些简单的接口规则直接使用。
应用程序接口(API,Application Programming Interface)是基于编程语言构建的结构,使开发人员更容易地创建复杂的功能。它们抽象了复杂的代码,并提供一些简单的接口规则直接使用。
当我们创建一个.net Core的Web API时,可以选择带有模板的,这样就自动生成了一个简单的API,而且调试的时候也会自动生成[swagger](https://swagger.io/ "swagger")打开网页可查看接口详细信息和测试。如下是ValueController的代码。
当我们创建一个.net Core的Web API时,可以选择带有模板的,这样就自动生成了一个简单的API,而且调试的时候也会自动生成[swagger](https://swagger.io/ "swagger")打开网页可查看接口详细信息和测试。如下是ValueController的代码。
```csharp
[Route("api/[controller]")] // 自定义特性,调用接口时可以"https://localhost/api/Values"(这里[controller]是继承ControllerBase的类名,也可以拼接很多,比如加[action]是方法名)
[Route("api/[controller]")] // 自定义特性,调用接口时可以"https://localhost/api/Values"(这里[controller]是继承ControllerBase的类名,也可以拼接很多,比如加[action]是方法名)
[ApiController]
public class ValuesController : ControllerBase
{
......@@ -19,7 +19,7 @@ public class ValuesController : ControllerBase
}
```
此时用GET调用接口会返回
此时用GET调用接口会返回
```json
[
"value1",
......@@ -27,19 +27,19 @@ public class ValuesController : ControllerBase
]
```
Web API还允许在方法中使用特性来切换请求方式和参数方式:
Web API还允许在方法中使用特性来切换请求方式和参数方式:
```csharp
// GET api/<ValuesController>/5
[HttpGet("{id}")] // 这里的{id}就是参数id
[HttpGet("{id}")] // 这里的{id}就是参数id
public string Get(int id)
{
return (id * 2).ToString();
}
// POST api/<ValuesController>
[HttpPost] // 这里用了POST方法
public void Post([FromBody] string value) //FromBody特性,curl -X POST "https://localhost/api/Values" -H "accept: */*" -H "Content-Type: application/json" -d "\"string\""
[HttpPost] // 这里用了POST方法
public void Post([FromBody] string value) //FromBody特性,curl -X POST "https://localhost/api/Values" -H "accept: */*" -H "Content-Type: application/json" -d "\"string\""
{
}
......@@ -102,16 +102,16 @@ public class TestController : ControllerBase
}
```
如上代码,在下列选项中,可以正确获取Test接口的是:
如上代码,在下列选项中,可以正确获取Test接口的是:
## 答案
## 答案
```
curl -X GET "https://localhost:44326/api/Test/Sum?num1=6&num2=6" -H "accept: text/plain"
curl -X GET "https://localhost:44326/api/Test/Copy/str" -H "accept: text/plain"
```
## 选项
## 选项
### A
......
......@@ -4,7 +4,9 @@
".NET Web API"
],
"children": [],
"export": [],
"export": [
"WebAPI.json"
],
"keywords_must": [],
"keywords_forbid": []
}
\ No newline at end of file
# -*- coding: utf-8 -*-
from src.tree import TreeWalker
from skill_tree.tree import TreeWalker
if __name__ == '__main__':
walker = TreeWalker("data", "csharp", "csharp")
......
uuid==1.30
\ No newline at end of file
skill-tree-parser~=0.0.5
\ No newline at end of file
import json
import logging
import os
import re
import subprocess
import sys
import uuid
import re
id_set = set()
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
def search_author(author_dict, username):
for key in author_dict:
names = author_dict[key]
if username in names:
return key
return username
def user_name(md_file, author_dict):
ret = subprocess.Popen([
"git", "log", md_file
], stdout=subprocess.PIPE)
lines = list(map(lambda l: l.decode(), ret.stdout.readlines()))
author_lines = []
for line in lines:
if line.startswith('Author'):
author_lines.append(line.split(' ')[1])
author_nick_name = author_lines[-1]
return search_author(author_dict, author_nick_name)
def load_json(p):
with open(p, 'r', encoding="utf-8") as f:
return json.loads(f.read())
def dump_json(p, j, exist_ok=False, override=False):
if os.path.exists(p):
if exist_ok:
if not override:
return
else:
logger.error(f"{p} already exist")
sys.exit(0)
with open(p, 'w+', encoding="utf8") as f:
f.write(json.dumps(j, indent=2, ensure_ascii=False))
def ensure_config(path):
config_path = os.path.join(path, "config.json")
if not os.path.exists(config_path):
node = {"keywords": []}
dump_json(config_path, node, exist_ok=True, override=False)
return node
else:
return load_json(config_path)
def parse_no_name(d):
p = r'(\d+)\.(.*)'
m = re.search(p, d)
try:
no = int(m.group(1))
dir_name = m.group(2)
except:
sys.exit(0)
return no, dir_name
def check_export(base, cfg):
flag = False
exports = []
for export in cfg.get('export', []):
ecfg_path = os.path.join(base, export)
if os.path.exists(ecfg_path):
exports.append(export)
else:
flag = True
if flag:
cfg["export"] = exports
return flag
class TreeWalker:
def __init__(
self, root,
tree_name,
title=None,
log=None,
authors=None,
enable_notebook=None,
ignore_keywords=False
):
self.ignore_keywords = ignore_keywords
self.authors = authors if authors else {}
self.enable_notebook = enable_notebook
self.name = tree_name
self.root = root
self.title = tree_name if title is None else title
self.tree = {}
self.logger = logger if log is None else log
def walk(self):
root = self.load_root()
root_node = {
"node_id": root["node_id"],
"keywords": root["keywords"],
"children": [],
"keywords_must": root["keywords_must"],
"keywords_forbid": root["keywords_forbid"]
}
self.tree[root["tree_name"]] = root_node
self.load_levels(root_node)
self.load_chapters(self.root, root_node)
for index, level in enumerate(root_node["children"]):
level_title = list(level.keys())[0]
level_node = list(level.values())[0]
level_path = os.path.join(self.root, f"{index + 1}.{level_title}")
self.load_chapters(level_path, level_node)
for index, chapter in enumerate(level_node["children"]):
chapter_title = list(chapter.keys())[0]
chapter_node = list(chapter.values())[0]
chapter_path = os.path.join(
level_path, f"{index + 1}.{chapter_title}")
self.load_sections(chapter_path, chapter_node)
for index, section_node in enumerate(chapter_node["children"]):
section_title = list(section_node.keys())[0]
full_path = os.path.join(
chapter_path, f"{index + 1}.{section_title}")
if os.path.isdir(full_path):
self.check_section_keywords(full_path)
self.ensure_exercises(full_path)
tree_path = os.path.join(self.root, "tree.json")
dump_json(tree_path, self.tree, exist_ok=True, override=True)
return self.tree
def sort_dir_list(self, dirs):
result = [self.extract_node_env(dir) for dir in dirs]
result.sort(key=lambda item: item[0])
return result
def load_levels(self, root_node):
levels = []
for level in os.listdir(self.root):
if not os.path.isdir(level):
continue
level_path = os.path.join(self.root, level)
num, config = self.load_level_node(level_path)
levels.append((num, config))
levels = self.resort_children(self.root, levels)
root_node["children"] = [item[1] for item in levels]
return root_node
def load_level_node(self, level_path):
config = self.ensure_level_config(level_path)
num, name = self.extract_node_env(level_path)
result = {
name: {
"node_id": config["node_id"],
"keywords": config["keywords"],
"children": [],
"keywords_must": config["keywords_must"],
"keywords_forbid": config["keywords_forbid"]
}
}
return num, result
def load_chapters(self, base, level_node):
chapters = []
for name in os.listdir(base):
full_name = os.path.join(base, name)
if os.path.isdir(full_name):
num, chapter = self.load_chapter_node(full_name)
chapters.append((num, chapter))
chapters = self.resort_children(base, chapters)
level_node["children"] = [item[1] for item in chapters]
return level_node
def load_sections(self, base, chapter_node):
sections = []
for name in os.listdir(base):
full_name = os.path.join(base, name)
if os.path.isdir(full_name):
num, section = self.load_section_node(full_name)
sections.append((num, section))
sections = self.resort_children(base, sections)
chapter_node["children"] = [item[1] for item in sections]
return chapter_node
def resort_children(self, base, children):
children.sort(key=lambda item: item[0])
for index, [number, element] in enumerate(children):
title = list(element.keys())[0]
origin = os.path.join(base, f"{number}.{title}")
posted = os.path.join(base, f"{index + 1}.{title}")
if origin != posted:
self.logger.info(f"rename [{origin}] to [{posted}]")
os.rename(origin, posted)
return children
def ensure_chapters(self):
for subdir in os.listdir(self.root):
self.ensure_level_config(subdir)
def load_root(self):
config_path = os.path.join(self.root, "config.json")
if not os.path.exists(config_path):
config = {
"tree_name": self.name,
"keywords": [],
"node_id": self.gen_node_id(),
"keywords_must": [],
"keywords_forbid": []
}
dump_json(config_path, config, exist_ok=True, override=True)
else:
config = load_json(config_path)
flag, result = self.ensure_node_id(config)
if flag:
dump_json(config_path, result, exist_ok=True, override=True)
return config
def ensure_level_config(self, path):
config_path = os.path.join(path, "config.json")
if not os.path.exists(config_path):
config = {
"node_id": self.gen_node_id()
}
dump_json(config_path, config, exist_ok=True, override=True)
else:
config = load_json(config_path)
flag, result = self.ensure_node_id(config)
if flag:
dump_json(config_path, config, exist_ok=True, override=True)
return config
def ensure_chapter_config(self, path):
config_path = os.path.join(path, "config.json")
if not os.path.exists(config_path):
config = {
"node_id": self.gen_node_id(),
"keywords": [],
"keywords_must": [],
"keywords_forbid": []
}
dump_json(config_path, config, exist_ok=True, override=True)
else:
config = load_json(config_path)
flag, result = self.ensure_node_id(config)
if flag:
dump_json(config_path, config, exist_ok=True, override=True)
return config
def ensure_section_config(self, path):
config_path = os.path.join(path, "config.json")
if not os.path.exists(config_path):
config = {
"node_id": self.gen_node_id(),
"keywords": [],
"children": [],
"export": []
}
dump_json(config_path, config, exist_ok=True, override=True)
else:
config = load_json(config_path)
flag, result = self.ensure_node_id(config)
if flag:
dump_json(config_path, result, exist_ok=True, override=True)
return config
def ensure_node_id(self, config):
flag = False
if "node_id" not in config or \
not config["node_id"].startswith(f"{self.name}-") or \
config["node_id"] in id_set:
new_id = self.gen_node_id()
id_set.add(new_id)
config["node_id"] = new_id
flag = True
for child in config.get("children", []):
child_node = list(child.values())[0]
f, _ = self.ensure_node_id(child_node)
flag = flag or f
return flag, config
def gen_node_id(self):
return f"{self.name}-{uuid.uuid4().hex}"
def extract_node_env(self, path):
try:
_, dir = os.path.split(path)
self.logger.info(path)
number, title = dir.split(".", 1)
return int(number), title
except Exception as error:
self.logger.error(f"目录 [{path}] 解析失败,结构不合法,可能是缺少序号")
# sys.exit(1)
raise error
def load_chapter_node(self, full_name):
config = self.ensure_chapter_config(full_name)
num, name = self.extract_node_env(full_name)
result = {
name: {
"node_id": config["node_id"],
"keywords": config["keywords"],
"children": [],
"keywords_must": config["keywords_must"],
"keywords_forbid": config["keywords_forbid"]
}
}
return num, result
def load_section_node(self, full_name):
config = self.ensure_section_config(full_name)
num, name = self.extract_node_env(full_name)
result = {
name: {
"node_id": config["node_id"],
"keywords": config["keywords"],
"children": config.get("children", []),
"keywords_must": config["keywords_must"],
"keywords_forbid": config["keywords_forbid"]
}
}
# if "children" in config:
# result["children"] = config["children"]
return num, result
def ensure_exercises(self, section_path):
config = self.ensure_section_config(section_path)
flag = False
for e in os.listdir(section_path):
base, ext = os.path.splitext(e)
_, source = os.path.split(e)
if ext != ".md":
continue
mfile = base + ".json"
meta_path = os.path.join(section_path, mfile)
md_file = os.path.join(section_path, e)
self.ensure_exercises_meta(meta_path, source, md_file)
export = config.get("export", [])
if mfile not in export and self.name != "algorithm":
export.append(mfile)
flag = True
config["export"] = export
if flag:
dump_json(os.path.join(section_path, "config.json"),
config, True, True)
for e in config.get("export", []):
full_name = os.path.join(section_path, e)
exercise = load_json(full_name)
if "exercise_id" not in exercise or exercise.get("exercise_id") in id_set:
eid = uuid.uuid4().hex
exercise["exercise_id"] = eid
dump_json(full_name, exercise, True, True)
else:
id_set.add(exercise["exercise_id"])
def ensure_exercises_meta(self, meta_path, source, md_file):
_, mfile = os.path.split(meta_path)
meta = None
if os.path.exists(meta_path):
with open(meta_path) as f:
content = f.read()
if content:
meta = json.loads(content)
if "exercise_id" not in meta:
meta["exercise_id"] = uuid.uuid4().hex
if "notebook_enable" not in meta:
meta["notebook_enable"] = self.default_notebook()
if "source" not in meta:
meta["source"] = source
if "author" not in meta:
meta["author"] = user_name(md_file, self.authors)
if "type" not in meta:
meta["type"] = "code_options"
if meta is None:
meta = {
"type": "code_options",
"author": user_name(md_file, self.authors),
"source": source,
"notebook_enable": self.default_notebook(),
"exercise_id": uuid.uuid4().hex
}
dump_json(meta_path, meta, True, True)
def default_notebook(self):
if self.enable_notebook is not None:
return self.enable_notebook
if self.name in ["python", "java", "c"]:
return True
else:
return False
def check_section_keywords(self, full_path):
if self.ignore_keywords:
return
config = self.ensure_section_config(full_path)
if not config.get("keywords", []):
self.logger.error(f"节点 [{full_path}] 的关键字为空,请修改配置文件写入关键字")
sys.exit(1)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册