فهرست منبع

Ameliorations diverses

olivier.massot 7 سال پیش
والد
کامیت
f07b8e2ff1

+ 2 - 1
.gitignore

@@ -2,4 +2,5 @@
 .project
 .pydevproject
 Output/
-htmlcov/
+htmlcov/
+*.accdb

+ 39 - 18
Viewer.py

@@ -12,20 +12,19 @@ from PyQt5.Qt import Qt, QEvent, QGraphicsScene, QPointF, QFileDialog, \
 from PyQt5.QtWidgets import QMainWindow, QGraphicsView
 from path import Path
 
-from core import AccessObject
 import core
 
 
 Ui_window, _ = uic.loadUiType(Path(__file__).parent / 'qt_viewer.ui')
 
 palette = {
-            "tables": QColor(240, 240, 20),
-            "queries": QColor(210, 50, 210),
-            "modules": QColor(220, 10, 33),
-            "relations": QColor(122, 50, 209),
-            "reports": QColor(25, 10, 220),
-            "forms": QColor(10, 180, 220),
-            "scripts": QColor(40, 220, 10),
+            "Table": QColor(240, 240, 20),
+            "Query": QColor(210, 50, 210),
+            "Module": QColor(220, 10, 33),
+            "Relation": QColor(122, 50, 209),
+            "Report": QColor(25, 10, 220),
+            "Form": QColor(10, 180, 220),
+            "Macro": QColor(40, 220, 10),
           }
 
 v_spacing = 120
@@ -76,7 +75,11 @@ class GraphicsObject(QGraphicsItemGroup):
         GraphicsObject.items.append(self)
 
     def setText(self):
-        self.label.setHtml("[{}]<br/><b>{}</b>".format(self.obj.type_, self.obj.nom))
+        self.label.setHtml(self.html())
+
+    def html(self):
+        return "[{}]<br/><b>{}</b>".format(self.obj.type_,
+                                             self.obj.nom)
 
     def topAnchorCenter(self):
         return self.mapToScene(QPointF(*self.topAnchorCoords))
@@ -113,6 +116,10 @@ class GraphicsRootObject(GraphicsObject):
         self._dep_emprise = 0
         self._ref_emprise = 0
 
+        pen = QPen(QColor("red"))
+        pen.setWidth(2)
+        self.rect.setPen(pen)
+
     def xleft(self):
         return 0
 
@@ -162,8 +169,8 @@ class GraphicsCloneDepObject(GraphicsDepObject):
                                                             and type(item) is GraphicsDepObject
                                                             or type(item) is GraphicsRefObject))
 
-    def setText(self):
-        self.label.setHtml("[{}]<br/><b>{}</b><br/>(!) Clône".format(self.obj.type_, self.obj.nom))
+    def html(self):
+        return super(GraphicsCloneDepObject, self).html() + "<br/>(!) Clône"
 
     @property
     def deps(self):
@@ -212,8 +219,8 @@ class GraphicsCloneRefObject(GraphicsRefObject):
                                                                     and type(item) is GraphicsDepObject
                                                                     or type(item) is GraphicsRefObject))
 
-    def setText(self):
-        self.label.setHtml("[{}]<br/><b>{}</b><br/>(!) Clône".format(self.obj.type_, self.obj.nom))
+    def html(self):
+        return super(GraphicsCloneRefObject, self).html() + "<br/>(!) Clône"
 
     @property
     def refs(self):
@@ -278,6 +285,8 @@ class Viewer(QMainWindow):
         self.ui.btn_save.clicked.connect(self.save_to_png)
         self.ui.treeWidget.itemClicked.connect(self.treeItemSelected)
 
+        self._title = "<unknown>"
+
         core.Analyse.report = self.update_progression
 
         self.ui.view.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
@@ -328,9 +337,8 @@ class Viewer(QMainWindow):
         gen.setFileName(fileName)
         gen.setSize(QSize(1000, 1000))
         gen.setViewBox(QRect(0, 0, 1000, 1000))
-        gen.setTitle("SVG Generator Example Drawing")
-        gen.setDescription("An SVG drawing created by the SVG Generator "
-                                    "Example provided with Qt.")
+        gen.setTitle("Access Analyser")
+        gen.setDescription("Access Analyser Report for {}".format(self._title))
         painter = QPainter(gen)
         self._scene.render(painter)
         painter.end()
@@ -348,20 +356,33 @@ class Viewer(QMainWindow):
             return
         self.run(source_dir)
 
-
     def run(self, source_dir):
+        source_dir = Path(source_dir)
+
+        self._title = source_dir
 
         self.ui.progressBar.setVisible(True)
         self.ui.lbl_repertoire.setText(source_dir)
         self.ui.txtPanel.clear()
         self.ui.treeWidget.clear()
 
+        if self.ui.radioRefsOnly.isChecked():
+            mode = core.REFS_ONLY
+        elif self.ui.radioDepsOnly.isChecked():
+            mode = core.DEPS_ONLY
+        else:
+            mode = core.DEPS_AND_REFS
+        print(mode)
+
         QApplication.setOverrideCursor(Qt.WaitCursor)
 
-        core.Analyse.run(source_dir)
+        core.Analyse.run(source_dir, mode)
 
         QApplication.restoreOverrideCursor()
 
+        if core.Analyse.duplicated_names:
+            QMessageBox.warning(self, "Risque d'instabilités", "Attention! Des doublons ont été trouvés dans les noms des objets suivants:\n{}".format(", ".join(core.Analyse.duplicated_names)))
+
         QMessageBox.information(self, "test", "{} objets chargés".format(len(core.Analyse.objects)))
 
         self.ui.progressBar.setVisible(False)

+ 87 - 23
core.py

@@ -9,13 +9,12 @@ from path import Path
 objects = []
 
 
-SUBSTR = {92: "\\", 47: "/", 58: ":", 42: "*", 63:"?", 34:"\"", 60:"<", 62:">", 124:"|" }
-def path_to_name(path):
-    name_ = path.name.stripext()
-    for ascii_code, char in SUBSTR.items():
-        name_ = name_.replace("[{}]".format(ascii_code), char)
-    return name_
 
+# TODO: gérer les cas où des objets de types différents portent le même nom. Ex: une table et un formulaire...
+# TODO: ignorer les commentaires dans le modules
+# TODO: ignorer les labels dans les formulaires et états
+
+# NB: une requete et une table ne peuvent pas porter le même nom.
 
 RXS = {}
 def getrx(nom):
@@ -35,9 +34,9 @@ def recurse(acc_obj):
     return deptree
 
 class AccessObject():
-    def __init__(self, type_, nom, sourcefile):
+    type_ = "<unknown>"
+    def __init__(self, nom, sourcefile):
         self.nom = nom
-        self.type_ = type_
         self.functions = []
         self.sourcefile = sourcefile
         self.deps = []
@@ -46,6 +45,10 @@ class AccessObject():
     def __repr__(self):
         return "<{}: {}>".format(self.type_, self.nom)
 
+    @classmethod
+    def from_file(cls, file):
+        return cls(AccessObject.path_to_name(file), file)
+
     def add_dep(self, obj):
         if not obj in self.deps:
             self.deps.append(obj)
@@ -54,11 +57,42 @@ class AccessObject():
         if not obj in self.refs:
             self.refs.append(obj)
 
+    SUBSTR = {92: "\\", 47: "/", 58: ":", 42: "*", 63:"?", 34:"\"", 60:"<", 62:">", 124:"|" }
+    @staticmethod
+    def path_to_name(path):
+        name_ = path.name.stripext()
+        for ascii_code, char in AccessObject.SUBSTR.items():
+            name_ = name_.replace("[{}]".format(ascii_code), char)
+        return name_
+
+class TableObject(AccessObject):
+    type_ = "Table"
+
+class QueryObject(AccessObject):
+    type_ = "Query"
+
+class FormObject(AccessObject):
+    type_ = "Form"
+
+class ReportObject(AccessObject):
+    type_ = "Report"
+
+class MacroObject(AccessObject):
+    type_ = "Macro"
+
+class ModuleObject(AccessObject):
+    type_ = "Module"
+
+class RelationObject(AccessObject):
+    type_ = "Relation"
+
+REFS_ONLY = 1
+DEPS_ONLY = 2
+DEPS_AND_REFS = 3
+
 class Analyse():
     objects = []
-
-    def __enter__(self):
-        pass
+    duplicated_names = []
 
     @staticmethod
     def report(current, total, msg=""):
@@ -69,49 +103,79 @@ class Analyse():
         pass
 
     @classmethod
-    def run(cls, source_dir):
+    def register(cls, obj):
+        if obj.nom in [other.nom for other in cls.objects] and not obj.nom in cls.duplicated_names:
+            cls.duplicated_names.append(obj.nom)
+        cls.objects.append(obj)
+
+    @classmethod
+    def run(cls, source_dir, mode=DEPS_AND_REFS):
         source_dir = Path(source_dir)
 
         cls.objects = []
+        cls.duplicated_names = []
 
         # Liste les objets à partir de l'arborescence du repertoire des sources
         cls.report(0, 100, "Analyse du répertoire")
 
-        for folder in ["queries", "forms", "relations", "reports", "scripts"]:
-            for file in Path(source_dir / folder).files("*.bas"):
-                cls.objects.append(AccessObject(folder, path_to_name(file), file))
+        for file in Path(source_dir / "forms").files("*.bas"):
+            obj = FormObject.from_file(file)
+            cls.register(obj)
+
+        for file in Path(source_dir / "reports").files("*.bas"):
+            obj = ReportObject.from_file(file)
+            cls.register(obj)
+
+        for file in Path(source_dir / "relations").files("*.bas"):
+            obj = RelationObject.from_file(file)
+            cls.register(obj)
+
+        for file in Path(source_dir / "scripts").files("*.bas"):
+            obj = MacroObject.from_file(file)
+            cls.register(obj)
+
+        for file in Path(source_dir / "queries").files("*.bas"):
+            obj = QueryObject.from_file(file)
+            cls.register(obj)
+
         for file in Path(source_dir / "tables").files("*.xml") + Path(source_dir / "tables").files("*.lnkd"):
-            cls.objects.append(AccessObject("tables", path_to_name(file), file))
+            obj = TableObject.from_file(file)
+            cls.register(obj)
 
         rx = re.compile(r"Sub|Function ([^(]+)\(")
         for file in Path(source_dir / "modules").files("*.bas"):
-            obj = AccessObject("modules", path_to_name(file), file)
+            obj = ModuleObject.from_file(file)
             obj.functions = [fname for fname in rx.findall(file.text()) if fname]
-            cls.objects.append(obj)
+            cls.register(obj)
 
         total = len(cls.objects)
         cls.report(0, total, "> {} objets trouvés".format(total))
 
         # met à jour les dependances en listant les noms d'objets mentionnés dans le fichier subject
         for i, subject in enumerate(cls.objects):
-            cls.report(i, total, "* {}".format(subject.nom))
+            cls.report(i, total, "* {}: {}".format(subject.type_, subject.nom))
 
             source = subject.sourcefile.text()
             for object_ in cls.objects:
                 if object_ is subject:
                     continue
                 if getrx(object_.nom).search(source):
-                    subject.add_dep(object_)
-                    object_.add_ref(subject)
+                    if mode in (DEPS_AND_REFS, DEPS_ONLY):
+                        subject.add_dep(object_)
+                    if mode in (DEPS_AND_REFS, REFS_ONLY):
+                        object_.add_ref(subject)
                     continue
                 for fname in object_.functions:
                     if getrx(fname).search(source):
-                        subject.add_dep(object_)
-                        object_.add_ref(subject)
+                        if mode in (DEPS_AND_REFS, DEPS_ONLY):
+                            subject.add_dep(object_)
+                        if mode in (DEPS_AND_REFS, REFS_ONLY):
+                            object_.add_ref(subject)
                         break
 
         cls.report(total, total, "Analyse terminée")
         cls.ended()
+
         return cls.objects
 
 

+ 55 - 1
qt_viewer.ui

@@ -99,6 +99,9 @@
             <iconset>
              <normaloff>dossier_32.png</normaloff>dossier_32.png</iconset>
            </property>
+           <property name="autoDefault">
+            <bool>true</bool>
+           </property>
           </widget>
          </item>
          <item>
@@ -354,7 +357,7 @@
           </property>
           <property name="icon">
            <iconset>
-            <normaloff>enregistrer_16.png</normaloff>enregistrer_16.png</iconset>
+            <normaloff>../access_cleaner/enregistrer_16.png</normaloff>../access_cleaner/enregistrer_16.png</iconset>
           </property>
          </widget>
         </item>
@@ -371,6 +374,57 @@
           </property>
          </spacer>
         </item>
+        <item>
+         <widget class="QGroupBox" name="groupBox">
+          <property name="minimumSize">
+           <size>
+            <width>200</width>
+            <height>0</height>
+           </size>
+          </property>
+          <property name="title">
+           <string/>
+          </property>
+          <layout class="QHBoxLayout" name="horizontalLayout_3">
+           <property name="leftMargin">
+            <number>3</number>
+           </property>
+           <property name="topMargin">
+            <number>3</number>
+           </property>
+           <property name="rightMargin">
+            <number>3</number>
+           </property>
+           <property name="bottomMargin">
+            <number>3</number>
+           </property>
+           <item>
+            <widget class="QRadioButton" name="radioDepsAndRefs">
+             <property name="text">
+              <string>Standard</string>
+             </property>
+             <property name="checked">
+              <bool>true</bool>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QRadioButton" name="radioDepsOnly">
+             <property name="text">
+              <string>Dépendances slt.</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QRadioButton" name="radioRefsOnly">
+             <property name="text">
+              <string>Références slt.</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
         <item>
          <widget class="QPushButton" name="btn_test">
           <property name="text">

+ 3 - 3
test/source/forms/frmChevaliers.bas

@@ -9,9 +9,9 @@ Begin Form
     GridY =10
     Width =6994
     DatasheetFontHeight =11
-    ItemSuffix =3
-    Right =9465
-    Bottom =7905
+    ItemSuffix =6
+    Right =14175
+    Bottom =12135
     DatasheetGridlinesColor =14806254
     RecSrcDt = Begin
         0xb856b6c1d312e540

+ 74 - 0
test/source/forms/frmFauxPositif.bas

@@ -0,0 +1,74 @@
+Version =20
+VersionRequired =20
+Begin Form
+    DividingLines = NotDefault
+    AllowDesignChanges = NotDefault
+    DefaultView =0
+    PictureAlignment =2
+    DatasheetGridlinesBehavior =3
+    GridY =10
+    Width =6994
+    DatasheetFontHeight =11
+    ItemSuffix =1
+    Right =14175
+    Bottom =12135
+    DatasheetGridlinesColor =14806254
+    RecSrcDt = Begin
+        0x3c4b55e0d512e540
+    End
+    DatasheetFontName ="Calibri"
+    FilterOnLoad =0
+    ShowPageMargins =0
+    DisplayOnSharePointSite =1
+    DatasheetAlternateBackColor =15921906
+    DatasheetGridlinesColor12 =0
+    FitToScreen =1
+    DatasheetBackThemeColorIndex =1
+    BorderThemeColorIndex =3
+    ThemeFontIndex =1
+    ForeThemeColorIndex =0
+    AlternateBackThemeColorIndex =1
+    AlternateBackShade =95.0
+    Begin
+        Begin Label
+            BackStyle =0
+            FontSize =11
+            FontName ="Calibri"
+            ThemeFontIndex =1
+            BackThemeColorIndex =1
+            BorderThemeColorIndex =0
+            BorderTint =50.0
+            ForeThemeColorIndex =0
+            ForeTint =50.0
+            GridlineThemeColorIndex =1
+            GridlineShade =65.0
+        End
+        Begin Section
+            Height =5952
+            Name ="Détail"
+            AutoHeight =1
+            AlternateBackColor =15921906
+            AlternateBackThemeColorIndex =1
+            AlternateBackShade =95.0
+            BackThemeColorIndex =1
+            Begin
+                Begin Label
+                    OverlapFlags =85
+                    Left =1587
+                    Top =396
+                    Width =3345
+                    Height =511
+                    BorderColor =8355711
+                    ForeColor =8355711
+                    Name ="Étiquette0"
+                    Caption ="Chevaliers"
+                    GridlineColor =10921638
+                    LayoutCachedLeft =1587
+                    LayoutCachedTop =396
+                    LayoutCachedWidth =4932
+                    LayoutCachedHeight =907
+                End
+            End
+        End
+    End
+End

+ 111 - 0
test/source/forms/frmWithmodule.bas

@@ -0,0 +1,111 @@
+Version =20
+VersionRequired =20
+Begin Form
+    DividingLines = NotDefault
+    AllowDesignChanges = NotDefault
+    DefaultView =0
+    PictureAlignment =2
+    DatasheetGridlinesBehavior =3
+    GridY =10
+    Width =6994
+    DatasheetFontHeight =11
+    Right =14175
+    Bottom =12135
+    DatasheetGridlinesColor =14806254
+    RecSrcDt = Begin
+        0x843791ded512e540
+    End
+    DatasheetFontName ="Calibri"
+    FilterOnLoad =0
+    ShowPageMargins =0
+    DisplayOnSharePointSite =1
+    DatasheetAlternateBackColor =15921906
+    DatasheetGridlinesColor12 =0
+    FitToScreen =1
+    DatasheetBackThemeColorIndex =1
+    BorderThemeColorIndex =3
+    ThemeFontIndex =1
+    ForeThemeColorIndex =0
+    AlternateBackThemeColorIndex =1
+    AlternateBackShade =95.0
+    Begin
+        Begin Label
+            BackStyle =0
+            FontSize =11
+            FontName ="Calibri"
+            ThemeFontIndex =1
+            BackThemeColorIndex =1
+            BorderThemeColorIndex =0
+            BorderTint =50.0
+            ForeThemeColorIndex =0
+            ForeTint =50.0
+            GridlineThemeColorIndex =1
+            GridlineShade =65.0
+        End
+        Begin TextBox
+            AddColon = NotDefault
+            FELineBreak = NotDefault
+            BorderLineStyle =0
+            Width =1701
+            LabelX =-1701
+            FontSize =11
+            FontName ="Calibri"
+            AsianLineBreak =1
+            BackThemeColorIndex =1
+            BorderThemeColorIndex =1
+            BorderShade =65.0
+            ThemeFontIndex =1
+            ForeThemeColorIndex =0
+            ForeTint =75.0
+            GridlineThemeColorIndex =1
+            GridlineShade =65.0
+        End
+        Begin Section
+            Height =5952
+            Name ="Détail"
+            AutoHeight =1
+            AlternateBackColor =15921906
+            AlternateBackThemeColorIndex =1
+            AlternateBackShade =95.0
+            BackThemeColorIndex =1
+            Begin
+                Begin TextBox
+                    OverlapFlags =85
+                    IMESentenceMode =3
+                    Left =1870
+                    Top =453
+                    Width =1927
+                    Height =315
+                    BorderColor =10921638
+                    ForeColor =4210752
+                    Name ="Texte4"
+                    ControlSource ="=count_chevalier()"
+                    GridlineColor =10921638
+
+                    LayoutCachedLeft =1870
+                    LayoutCachedTop =453
+                    LayoutCachedWidth =3797
+                    LayoutCachedHeight =768
+                    Begin
+                        Begin Label
+                            OverlapFlags =85
+                            Left =963
+                            Top =453
+                            Width =705
+                            Height =315
+                            BorderColor =8355711
+                            ForeColor =8355711
+                            Name ="Étiquette5"
+                            Caption ="Texte4"
+                            GridlineColor =10921638
+                            LayoutCachedLeft =963
+                            LayoutCachedTop =453
+                            LayoutCachedWidth =1668
+                            LayoutCachedHeight =768
+                        End
+                    End
+                End
+            End
+        End
+    End
+End

+ 9 - 0
test/source/modules/FauxPosit.bas

@@ -0,0 +1,9 @@
+Option Compare Database
+
+Public Sub faux_positif()
+
+    MsgBox "Le commentaire suivant n'est pas sensé être compté"
+    
+    'tblTypesChateaux
+
+End Sub

+ 7 - 0
test/source/modules/General.bas

@@ -0,0 +1,7 @@
+Option Compare Database
+
+Public Function count_chevalier()
+
+    count_chevalier = DCount("id", "Chevaliers", "")
+
+End Function

+ 3 - 0
test/source/modules/tblChateaux.bas

@@ -0,0 +1,3 @@
+Option Compare Database
+
+'essai d'un doublon de noms avec une table

+ 46 - 0
test/source/tables/USysOpenAccess.xml

@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:od="urn:schemas-microsoft-com:officedata">
+<xsd:schema>
+<xsd:element name="dataroot">
+<xsd:complexType>
+<xsd:sequence>
+<xsd:element ref="USysOpenAccess" minOccurs="0" maxOccurs="unbounded"/>
+</xsd:sequence>
+<xsd:attribute name="generated" type="xsd:dateTime"/>
+</xsd:complexType>
+</xsd:element>
+<xsd:element name="USysOpenAccess">
+<xsd:annotation>
+<xsd:appinfo/>
+</xsd:annotation>
+<xsd:complexType>
+<xsd:sequence>
+<xsd:element name="key" minOccurs="0" od:jetType="text" od:sqlSType="nvarchar">
+<xsd:simpleType>
+<xsd:restriction base="xsd:string">
+<xsd:maxLength value="255"/>
+</xsd:restriction>
+</xsd:simpleType>
+</xsd:element>
+<xsd:element name="val" minOccurs="0" od:jetType="text" od:sqlSType="nvarchar">
+<xsd:simpleType>
+<xsd:restriction base="xsd:string">
+<xsd:maxLength value="255"/>
+</xsd:restriction>
+</xsd:simpleType>
+</xsd:element>
+</xsd:sequence>
+</xsd:complexType>
+</xsd:element>
+</xsd:schema>
+<dataroot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" generated="2018-02-27T16:27:17">
+<USysOpenAccess>
+<key>include_tables</key>
+<val>USysOpenAccess</val>
+</USysOpenAccess>
+<USysOpenAccess>
+<key>sources_date</key>
+<val>27/02/2018 14:52:41</val>
+</USysOpenAccess>
+</dataroot>
+</root>

BIN
test/test.laccdb


BIN
test/test.zip