Table of Contents
1. Main program
2. Activity class
3. Task list JSON file
4. Output template file
Home Backend Development Python Tutorial How to implement critical path and seven-grid graph calculation in Python

How to implement critical path and seven-grid graph calculation in Python

Apr 26, 2023 pm 05:28 PM
python

1. Main program

The main program mainly implements a Project class, which contains methods for calculating critical paths and seven-frame diagrams. The specific implementation method is as follows:

1. An Activity class is defined, which contains attributes such as activity id, name, duration, and immediate task list.

2. A Project class is defined, which includes attributes such as activity list, project duration, log, etc., as well as methods such as calculating critical path, calculating seven-frame diagram, calculating total float time, and calculating free float time.

3. Read activity information from the JSON file, create a Project object and add activities.

4. Call the calculate method of the Project object to calculate the earliest start time, latest start time and other data of each activity.

5. Call the calculate_critical_path method of the Project object to calculate the critical path.

6. Call the calculate_project_duration method of the Project object to calculate the total project duration.

7. Use Jinja2 template engine to generate the activity list of the project and set the key path

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

import json 

from datetime import datetime 

from typing import List 

   

import graphviz 

from jinja2 import Template 

   

from activity import Activity 

   

   

class Project: 

    def __init__(self): 

        self.activities: List[Activity] = [] 

        self.duration = 0 

        self.logger = [] 

   

    def log(self, log: str) -> None: 

        self.logger.append(log) 

   

    def add_activity(self, activity: Activity) -> None: 

        ""

        添加一个活动到项目中 

        :param 

        activity: 待添加的活动 

        """        # 将活动添加到项目中 

        self.activities.append(activity) 

   

    def calculate(self) -> None: 

        """ 计算整个项目的关键信息 

        :return: None 

        """        self.calculate_successor() 

        self._calculate_forward_pass()  # 计算正推法 

        self._calculate_backward_pass()  # 计算倒推法 

        self._calculate_total_floats()  # 计算总浮动时间 

        self._calculate_free_floats()  # 计算自由浮动时间 

   

    def calculate_successor(self) -> None: 

        self.log("开始计算紧后活动"

        for act in self.activities: 

            for pred in act.predecessors: 

                for act_inner in self.activities: 

                    if act_inner.id == pred: 

                        act_inner.successors.append(act.id) 

   

    def _calculate_forward_pass(self) -> None: 

        self.log("## 开始正推法计算"

        # 进入 while 循环,只有当所有活动的最早开始时间和最早完成时间都已经计算出来时,才会退出循环 

        while not self._is_forward_pass_calculated(): 

            # 遍历每个活动 

            for activity in self.activities: 

                # 如果活动的最早开始时间已经被计算过,则跳过 

                if activity.est is not None: 

                    continue 

                # 如果活动没有前置活动, 则从1开始计算最早开始时间和最早结束时间 

                if not activity.predecessors: 

                    activity.est = 1 

                    activity.eft = activity.est + activity.duration - 1 

                    self.log( 

                        f"活动 {activity.name} 没有紧前活动,设定最早开始时间为1, 并根据工期计算最早结束时间为{activity.eft}"

                else

                    # 计算当前活动的所有前置活动的最早完成时间 

                    predecessors_eft = [act.eft for act in self.activities if 

                                        act.id in activity.predecessors and act.eft is not None] 

                    # 如果当前活动的所有前置活动的最早完成时间都已经计算出来,则计算当前活动的最早开始时间和最早完成时间 

                    if len(predecessors_eft) == len(activity.predecessors): 

                        activity.est = max(predecessors_eft) + 1 

                        activity.eft = activity.est + activity.duration - 1 

                        self.log( 

                            f"活动 {activity.name} 紧前活动已完成正推法计算, 开始日期按最早开始时间里面最大的,"

                            f"设定为{activity.est}并根据工期计算最早结束时间为{activity.eft}"

   

                        # 更新项目总持续时间为最大最早完成时间 

        self.duration = max([act.eft for act in self.activities]) 

   

    def _calculate_backward_pass(self) -> None: 

        """ 计算倒推法 

        :return: None 

        ""

        self.log("## 开始倒推法计算")  # 输出提示信息 

        # 进入 while 循环,只有当所有活动的最晚开始时间和最晚完成时间都已经计算出来时,才会退出循环 

        while not self._is_backward_pass_calculated(): 

            # 遍历每个活动 

            for act in reversed(self.activities): 

                # 如果活动的最晚开始时间已经被计算过,则跳过 

                if act.lft is not None: 

                    continue 

                # 如果活动没有后继活动, 则从总持续时间开始计算最晚开始时间和最晚结束时间 

                if not act.successors: 

                    act.lft = self.duration 

                    act.lst = act.lft - act.duration + 1 

                    self.log(f"活动 {act.name} 没有紧后活动,按照正推工期设定最晚结束时间为{act.lft},"

                             f"并根据工期计算最晚开始时间为{act.lst}"

                else

                    # 计算当前活动的所有后继活动的最晚开始时间 

                    successors_lst = self._calculate_lst(act) 

                    # 如果当前活动的所有后继活动的最晚开始时间都已经计算出来,则计算当前活动的最晚开始时间和最晚完成时间 

                    if len(successors_lst) == len(act.successors): 

                        act.lft = min(successors_lst) - 1 

                        act.lst = act.lft - act.duration + 1 

                        self.log(f"活动 {act.name} 紧后活动计算完成,按照倒推工期设定最晚结束时间为{act.lft},"

                                 f"并根据工期计算最晚开始时间为{act.lst}"

        # 更新项目总持续时间为最大最晚完成时间 

        self.duration = max([act.lft for act in self.activities]) 

   

    def _calculate_lst(self, activity: Activity) -> List[int]: 

        """计算某一活动的所有最晚开始时间 

        :param activity: 活动对象 

        :return: 最晚开始时间列表 

        """        rst = []  # 初始化结果列表 

        for act in activity.successors:  # 遍历该活动的后继活动 

            for act2 in self.activities:  # 遍历所有活动 

                if act2.id == act and act2.lst is not None:  # 如果找到了该后继活动且其最晚开始时间不为空 

                    rst.append(act2.lst)  # 将最晚开始时间加入结果列表 

        return rst  # 返回结果列表 

   

    def _is_forward_pass_calculated(self) -> bool: 

        """ 判断整个项目正推法计算已经完成 

        :return: 若已计算正向传递则返回True,否则返回False 

        ""

        for act in self.activities:  # 遍历所有活动 

            if act.est is None or act.eft is None:  # 如果该活动的最早开始时间或最早完成时间为空 

                return False  # 则返回False,表示还未计算正向传递 

        return True  # 如果所有活动的最早开始时间和最早完成时间都已计算,则返回True,表示已计算正向传递 

   

    def _is_backward_pass_calculated(self) -> bool: 

        """ 判断整个项目倒推法计算已经完成 

        :return: 若已计算倒推法则返回True,否则返回False 

        """        for act in self.activities:  # 遍历所有活动 

            if act.lst is None or act.lft is None:  # 如果该活动的最晚开始时间或最晚完成时间为空 

                return False  # 则返回False,表示还未计算倒推法 

        return True  # 如果所有活动的最晚开始时间和最晚完成时间都已计算,则返回True,表示已计算倒推法 

   

    def _calculate_total_floats(self) -> None: 

        """ 计算所有活动的总浮动时间 

        :return: None 

         ""

        self.log(f"## 开始计算项目所有活动的总浮动时间"

        for act in self.activities:  # 遍历所有活动 

            if act.est is not None and act.lst is not None:  # 如果该活动的最早开始时间和最晚开始时间都已计算 

                act.tf = act.lst - act.est  # 则计算该活动的总浮动时间 

                self.log(f"计算{act.name}的总浮动时间" + f"最晚开始时间{act.lst} - 最早开始时间{act.est} = {act.tf}", ) 

            else:  # 如果该活动的最早开始时间或最晚开始时间为空 

                act.tf = None  # 则将该活动的总浮动时间设为None 

   

    def _calculate_free_floats(self) -> None: 

        """ 计算所有活动的自由浮动时间 

        :return: None 

        """        self.log(f"## 开始计算项目所有活动的自由浮动时间")  # 输出提示信息 

        for act in self.activities:  # 遍历所有活动 

            if act.tf == 0:  # 如果该活动的总浮动时间为0 

                self.log(f"计算{act.name}的自由浮动时间" + f"因为{act.name}的总浮动时间为0,自由浮动时间为0")  # 输出提示信息 

                act.ff = 0  # 则将该活动的自由浮动时间设为0 

            elif act.tf > 0:  # 如果该活动的总浮动时间大于0 

                self.log(f"计算{act.name}的自由浮动时间")  # 输出提示信息 

                self.log(f"- {act.name}的总浮动时间{act.tf} > 0,")  # 输出提示信息 

                tmp = []  # 初始化临时列表 

                for act2 in self.activities:  # 遍历所有活动 

                    if act2.id in act.successors:  # 如果该活动是该活动的紧后活动 

                        self.log(f"- {act.name}的紧后活动{act2.name}的自由浮动动时间为{act2.tf}")  # 输出提示信息 

                        tmp.append(act2.tf)  # 将该紧后活动的自由浮动时间加入临时列表 

                if len(tmp) != 0:  # 如果临时列表不为空 

                    act.ff = act.tf - max(tmp)  # 则计算该活动的自由浮动时间 

                    if act.ff < 0: 

                        act.ff = 0 

                    self.log(f"- 用活动自己的总浮动{act.tf}减去多个紧后活动总浮动的最大值{max(tmp)} = {act.ff}"

                else:  # 如果临时列表为空 

                    act.ff = act.tf  # 则将该活动的自由浮动时间设为总浮动时间 

   

    def calculate_critical_path(self) -> List[Activity]: 

        """ 计算整个项目的关键路径 

        :return: 整个项目的关键路径 

        """        ctc_path = []  # 初始化关键路径列表 

        for act in self.activities:  # 遍历所有活动 

            if act.tf == 0:  # 如果该活动的总浮动时间为0 

                ctc_path.append(act)  # 则将该活动加入关键路径列表 

        return ctc_path  # 返回关键路径列表 

   

    def calculate_project_duration(self) -> int: 

        """ 计算整个项目的持续时间 

        :return: 整个项目的持续时间 

        """        return max(activity.eft for activity in self.activities)  # 返回所有活动的最早完成时间中的最大值,即整个项目的持续时间 

   

   

# 从JSON文件中读取活动信息 

with open(&#39;activities.json&#39;, &#39;r&#39;, encoding=&#39;utf-8&#39;) as f: 

    activities_data = json.load(f) 

   

# 创建Project对象并添加活动 

project = Project() 

for activity_data in activities_data: 

    activity = Activity( 

        activity_data[&#39;id&#39;], 

        activity_data[&#39;name&#39;], 

        activity_data[&#39;duration&#39;], 

        activity_data[&#39;predecessors&#39;] 

    )    project.add_activity(activity) 

   

# 计算每个活动的最早开始时间、最晚开始时间等数据 

project.calculate() 

   

# 计算关键路径和项目总工期 

critical_path = project.calculate_critical_path() 

project_duration = project.calculate_project_duration() 

   

# 生成项目的活动清单 

with open(&#39;template.html&#39;, &#39;r&#39;, encoding=&#39;utf-8&#39;) as f: 

    template = Template(f.read()) 

html = template.render( 

    activities=project.activities, 

    critical_path=critical_path, 

    project_duration=project_duration, 

    log=project.logger 

   

# 生成项目进度网络图 

aon_graph = graphviz.Digraph(format=&#39;png&#39;, graph_attr={&#39;rankdir&#39;: &#39;LR&#39;}) 

for activity in project.activities: 

    aon_graph.node(str(activity.id), activity.name) 

    for predecessor in activity.predecessors: 

        aon_graph.edge(str(predecessor), str(activity.id)) 

   

timestamp = datetime.now().strftime(&#39;%Y%m%d%H%M%S&#39;) 

   

aon_filename = f"aon_{timestamp}" 

   

aon_graph.render(aon_filename) 

   

# 将项目进度网络图插入到HTML文件中 

aon_image = f&#39;<img src="{aon_filename}.png" alt="Precedence Diagramming Method: AON">&#39; 

html = html.replace(&#39;<p>Precedence Diagramming Method: AON: <br/>[image]</p>&#39;, 

                    &#39;<p>紧前关系绘图法: AON: <br/>&#39; + aon_image + &#39;</p>&#39;) 

   

filename = datetime.now().strftime(&#39;%Y%m%d%H%M%S&#39;) + &#39;.html&#39; 

with open(filename, &#39;w&#39;, encoding=&#39;utf-8&#39;) as f: 

    f.write(html)

Copy after login

2. Activity class

Program name: activity.py

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

class Activity: 

    ""

    活动类,用于表示项目中的一个活动。 

   

    Attributes:        id (int): 活动的唯一标识符。 

        name (str): 活动的名称。 

        duration (int): 活动的持续时间。 

        predecessors (List[int]): 活动的前置活动列表,存储前置活动的id。 

        est (int): 活动的最早开始时间。 

        lst (int): 活动的最晚开始时间。 

        eft (int): 活动的最早完成时间。 

        lft (int): 活动的最晚完成时间。 

        tf (int): 活动的总浮动时间。 

        ff (int): 活动的自由浮动时间。 

        successors (List[int]): 活动的后继活动列表,存储后继活动的Activity对象。 

    ""

    def __init__(self, id: int, name: str, duration: int, predecessors: List[int]): 

        ""

        初始化活动对象。 

   

        Args:            id (int): 活动的唯一标识符。 

            name (str): 活动的名称。 

            duration (int): 活动的持续时间。 

            predecessors (List[int]): 活动的前置活动列表,存储前置活动的id。 

        """        self.id = id 

        self.name = name 

        self.duration = duration 

        self.predecessors = predecessors 

        self.est = None 

        self.lst = None 

        self.eft = None 

        self.lft = None 

        self.tf = None 

        self.ff = None 

        self.successors = [] 

   

    def __str__(self): 

        return f"id: {self.id}, name: {self.name}, est: {self.est}, lst: {self.lst}, eft: {self.eft}, lft: {self.lft}," 

        + f"successors: {self.successors}"

Copy after login

3. Task list JSON file

File name: activities.json

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

  {    "id": 1, 

    "name": "A"

    "duration": 2, 

    "predecessors": [] 

  }, 

  

    "id": 9, 

    "name": "A2"

    "duration": 3, 

    "predecessors": [] 

  }, 

    

    "id": 10, 

    "name": "A3"

    "duration": 2, 

    "predecessors": [] 

  }, 

  

    "id": 2, 

    "name": "B"

    "duration": 3, 

    "predecessors": [ 

      1, 

      

    

  }, 

  

    "id": 3, 

    "name": "C"

    "duration": 4, 

    "predecessors": [ 

      

    

  }, 

  

    "id": 4, 

    "name": "D"

    "duration": 2, 

    "predecessors": [ 

      2,10 

    

  }, 

  

    "id": 5, 

    "name": "E"

    "duration": 3, 

    "predecessors": [ 

      

    

  }, 

  

    "id": 6, 

    "name": "F"

    "duration": 2, 

    "predecessors": [ 

      

    

  }, 

  

    "id": 7, 

    "name": "G"

    "duration": 3, 

    "predecessors": [ 

      4, 

      

    

  }, 

  

    "id": 8, 

    "name": "H"

    "duration": 2, 

    "predecessors": [ 

      6, 

      

    

  }, 

  

    "id": 11, 

    "name": "H2"

    "duration": 4, 

    "predecessors": [ 

      6, 

      

    

  

]

Copy after login

4. Output template file

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

<!DOCTYPE html> 

<html> 

<head> 

    <meta charset="UTF-8"

    <title>PMP关键路径计算</title> 

    <style>        table { 

            border-collapse: collapse; 

            width: 100%; 

        

   

        th, td { 

            border: 1px solid black; 

            padding: 8px; 

            text-align: center; 

        

   

        th { 

            background-color: #4CAF50; 

            color: white; 

        

   

        .critical { 

            background-color: #ffcccc; 

        

    </style> 

</head> 

<body> 

<h2>活动清单</h2> 

<table> 

    <tr> 

        <th>ID</th> 

        <th>活动名</th> 

        <th>持续时间</th> 

        <th>紧前活动</th> 

        <th>紧后活动</th> 

        <th>最早开始时间EST</th> 

        <th>最早结束时间EFT</th> 

        <th>最晚开始时间LST</th> 

        <th>最晚结束时间LFT</th> 

        <th>总浮动时间TF</th> 

        <th>自由浮动时间FF</th> 

    </tr> 

    {% for activity in activities %} 

    <tr {% if activity in critical_path %}class="critical" {% endif %}> 

        <td>{{ activity.id }}</td> 

        <td>{{ activity.name }}</td> 

        <td>{{ activity.duration }}</td> 

        <td>            {% for predecessor in activity.predecessors %} 

            {% for act in activities %} 

            {% if act.id == predecessor %} 

            {{ act.name }} 

            {% endif %} 

            {% endfor %} 

            {% if not loop.last %}, {% endif %} 

            {% endfor %} 

        </td> 

        <td>            {% for successor in activity.successors %} 

            {% for act in activities %} 

   

            {% if act.id == successor %} 

            {{ act.name }} 

            {% endif %} 

            {% endfor %} 

            {% if not loop.last %}, {% endif %} 

            {% endfor %} 

        </td> 

        <td>{{ activity.est }}</td> 

        <td>{{ activity.eft }}</td> 

        <td>{{ activity.lst }}</td> 

        <td>{{ activity.lft }}</td> 

        <td>{{ activity.tf }}</td> 

        <td>{{ activity.ff }}</td> 

    </tr> 

    {% endfor %} 

</table> 

<p>关键路径是: {% for activity in critical_path %}{{ activity.name }}{% if not loop.last %} -> {% endif %}{% endfor 

    %}</p> 

<p>项目总工期: {{ project_duration }}</p> 

<p>Precedence Diagramming Method: AON: <br/>[image]</p> 

<p> 

<table> 

    <tr> 

        <th>执行过程</th> 

    </tr> 

    {% for i in log %} 

    <tr> 

        <td >{{i}}</td> 

    </tr> 

    {% endfor %} 

</table> 

</p> 

</body> 

</html>

Copy after login

The above is the detailed content of How to implement critical path and seven-grid graph calculation in Python. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

PHP and Python: Different Paradigms Explained PHP and Python: Different Paradigms Explained Apr 18, 2025 am 12:26 AM

PHP is mainly procedural programming, but also supports object-oriented programming (OOP); Python supports a variety of paradigms, including OOP, functional and procedural programming. PHP is suitable for web development, and Python is suitable for a variety of applications such as data analysis and machine learning.

Choosing Between PHP and Python: A Guide Choosing Between PHP and Python: A Guide Apr 18, 2025 am 12:24 AM

PHP is suitable for web development and rapid prototyping, and Python is suitable for data science and machine learning. 1.PHP is used for dynamic web development, with simple syntax and suitable for rapid development. 2. Python has concise syntax, is suitable for multiple fields, and has a strong library ecosystem.

Can vs code run in Windows 8 Can vs code run in Windows 8 Apr 15, 2025 pm 07:24 PM

VS Code can run on Windows 8, but the experience may not be great. First make sure the system has been updated to the latest patch, then download the VS Code installation package that matches the system architecture and install it as prompted. After installation, be aware that some extensions may be incompatible with Windows 8 and need to look for alternative extensions or use newer Windows systems in a virtual machine. Install the necessary extensions to check whether they work properly. Although VS Code is feasible on Windows 8, it is recommended to upgrade to a newer Windows system for a better development experience and security.

Is the vscode extension malicious? Is the vscode extension malicious? Apr 15, 2025 pm 07:57 PM

VS Code extensions pose malicious risks, such as hiding malicious code, exploiting vulnerabilities, and masturbating as legitimate extensions. Methods to identify malicious extensions include: checking publishers, reading comments, checking code, and installing with caution. Security measures also include: security awareness, good habits, regular updates and antivirus software.

How to run programs in terminal vscode How to run programs in terminal vscode Apr 15, 2025 pm 06:42 PM

In VS Code, you can run the program in the terminal through the following steps: Prepare the code and open the integrated terminal to ensure that the code directory is consistent with the terminal working directory. Select the run command according to the programming language (such as Python's python your_file_name.py) to check whether it runs successfully and resolve errors. Use the debugger to improve debugging efficiency.

Can visual studio code be used in python Can visual studio code be used in python Apr 15, 2025 pm 08:18 PM

VS Code can be used to write Python and provides many features that make it an ideal tool for developing Python applications. It allows users to: install Python extensions to get functions such as code completion, syntax highlighting, and debugging. Use the debugger to track code step by step, find and fix errors. Integrate Git for version control. Use code formatting tools to maintain code consistency. Use the Linting tool to spot potential problems ahead of time.

Can vscode be used for mac Can vscode be used for mac Apr 15, 2025 pm 07:36 PM

VS Code is available on Mac. It has powerful extensions, Git integration, terminal and debugger, and also offers a wealth of setup options. However, for particularly large projects or highly professional development, VS Code may have performance or functional limitations.

Can vscode run ipynb Can vscode run ipynb Apr 15, 2025 pm 07:30 PM

The key to running Jupyter Notebook in VS Code is to ensure that the Python environment is properly configured, understand that the code execution order is consistent with the cell order, and be aware of large files or external libraries that may affect performance. The code completion and debugging functions provided by VS Code can greatly improve coding efficiency and reduce errors.

See all articles