]> Softwares of Agnibho - ddstorm.git/blob - compile.py
Added documentation
[ddstorm.git] / compile.py
1 #! /usr/bin/python3
2
3 '''
4 This module converts text differetial diagnosis files
5 to a simplified modular file format that can be used
6 by the program.
7 '''
8 '''
9 Copyright (c) 2015 Agnibho Mondal
10 All rights reserved
11
12 This file is part of DDStorm.
13
14 DDStorm is free software: you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 DDStorm is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with DDStorm. If not, see <http://www.gnu.org/licenses/>.
26 '''
27
28 import os
29 import logging
30 import re
31 from operator import itemgetter
32 from fnmatch import fnmatch
33
34 from conf import Conf
35 from const import *
36 from alias import Alias
37
38 logging.basicConfig(filename=LOG_FILE)
39
40 class Compile:
41 '''
42 This class creates a compiler for the DDStorm
43 that compiles the text files containing list of
44 differential diagnosis to simplified modular
45 data files usable by the program.
46 '''
47
48 def __init__(self, conf=False):
49 '''
50 The constructor optionally accepts a configuration.
51 If none is provided it creates a default configuration.
52
53 Parameters:
54 conf - A dictionary containing configuration options
55 '''
56 if(conf):
57 self._conf=conf
58 else:
59 self._conf=Conf()
60 self.clean=True
61
62 def compile(self):
63 ''' Compile the text files to DDStorm modules. '''
64 self.source=set()
65 self.custom=set()
66 self.alias=Alias(self._conf)
67
68 # Loop over library files and add *.txt files to source
69 for path, subdirs, files in os.walk(self._conf.get("library_path")):
70 for name in files:
71 if(fnmatch(name, "*.txt")):
72 self.source.add(os.path.join(path, name))
73
74 # Loop over custom files and add *.txt files to custom
75 for path, subdirs, files in os.walk(self._conf.get("custom_path")):
76 for name in files:
77 if(fnmatch(name, "*.txt")):
78 self.custom.add(os.path.join(path, name))
79
80 # Create module directory if not already present and delete all module files
81 if(not os.path.isdir(self._conf.get("module_path"))):
82 os.makedirs(self._conf.get("module_path"))
83 for f in os.listdir(self._conf.get("module_path")):
84 if(fnmatch(f, "*.module")):
85 os.unlink(self._conf.get("module_path")+f)
86
87 # Create a regex for calculating priority from filename
88 self.priorityRegex=re.compile("(?<=\.)\d+$")
89
90 # First sort files by priority then compile them to module
91 for src in self._sortPriority(self.source):
92 self._makeModule(src)
93 for src in self._sortPriority(self.custom):
94 self._makeModule(src)
95
96 def _sortPriority(self, files):
97 ''' Sort data files based on their priority settings. '''
98 ls=[]
99 # Loop over the files
100 for addr in files:
101 # Format the file name
102 name=os.path.splitext(os.path.basename(addr))[0].lower().replace("_"," ").replace("-", " ")
103 # Search for priority tag on file name
104 m=re.search(self.priorityRegex, name)
105 # Add to ls as (symptom name, priority number, file name) with default priority of 100
106 if(m):
107 ls.append((name.replace("."+m.group(), ""), int(m.group()), addr))
108 else:
109 ls.append((name, 100, addr))
110 # Sort the file list, first by the symptom name, then by the priority number
111 ls.sort(reverse=True)
112 if(ls):
113 return(list(zip(*ls))[2])
114 else:
115 return ls
116
117 def _makeModule(self, src):
118 ''' Create application usable modules from data files. '''
119 # Format the file name
120 module=os.path.splitext(os.path.basename(src))[0].lower().replace("_"," ").replace("-", " ")
121 # Remove the priority tag from file name
122 m=re.search(self.priorityRegex, module)
123 if(m):
124 module=module.replace("."+m.group(), "")
125 # Create the module file name
126 modFile=self._conf.get("module_path")+module+".module"
127 modFlag=False
128 # Loop over both files, the source data file and the target module file
129 with open(src, "r") as sf, open(modFile, "a") as tf:
130 # Ignore lines starting with ! or #, + and - has special meaning, write other lines to module. Log the errors.
131 for line in sf:
132 line=line.strip().split("#")[0]
133 if(len(line)==0):
134 pass
135 elif(line.startswith("!")):
136 pass
137 elif(line.startswith("#")):
138 pass
139 elif(line.startswith("+")):
140 modFlag=True
141 elif(line.startswith("-")):
142 modFlag=True
143 elif(line.replace(" ","").replace("-","").replace("_","").replace("'","").isalnum()):
144 print(self.alias.get(line).capitalize(), file=tf)
145 else:
146 self.clean=False
147 logging.warning("Syntax error in file '"+src+"': "+line)
148 # Deal with special lines
149 if(modFlag):
150 modFlag=False
151 with open(src, "r") as f:
152 for line in f:
153 line=line.strip().split("#")[0]
154 if(line[1:].replace(" ","").replace("-","").replace("_","").replace("'","").isalnum()):
155 # If line starts with + add it to the module file
156 if(line.startswith("+")):
157 with open(modFile, "r") as fn:
158 text=fn.read()
159 with open(modFile, "w") as fn:
160 print(self.alias.get(line[1:]).capitalize()+"\n"+text, file=fn)
161 # If line starts with - remove corresponding item from the module file
162 elif(line.startswith("-")):
163 with open(modFile, "r") as fn:
164 text=fn.read()
165 text=text.replace(self.alias.get(line[1:]).capitalize()+"\n", "")
166 with open(modFile, "w") as fn:
167 print(text, file=fn)
168
169 def is_clean(self):
170 '''Report if compilation ended successfully'''
171 return self.clean
172
173 def main():
174 ''' Compile the data files into formatted module files '''
175 c=Compile().compile()
176
177 if(__name__=="__main__"):
178 main()